Browse Source

初始化代码仓库

Jinhui Zhu 5 years ago
commit
3b6b495adc
73 changed files with 13230 additions and 0 deletions
  1. 5 0
      README.txt
  2. 113 0
      app.js
  3. 19 0
      app.json
  4. 153 0
      app.wxss
  5. 184 0
      colorui/animation.wxss
  6. 54 0
      colorui/components/cu-custom.js
  7. 4 0
      colorui/components/cu-custom.json
  8. 16 0
      colorui/components/cu-custom.wxml
  9. 1 0
      colorui/components/cu-custom.wxss
  10. 36 0
      colorui/icon.wxss
  11. 3941 0
      colorui/main.wxss
  12. 51 0
      common.js
  13. 83 0
      config.js
  14. BIN
      images/logo.png
  15. BIN
      images/share.png
  16. BIN
      images/tabbar/about.png
  17. BIN
      images/tabbar/about_cur.png
  18. BIN
      images/tabbar/component.png
  19. BIN
      images/tabbar/component_cur.png
  20. BIN
      images/theme/10001.jpg
  21. BIN
      images/theme/10002.jpg
  22. BIN
      images/theme/10003.jpg
  23. BIN
      images/theme/10004.jpg
  24. BIN
      images/theme/10005.jpg
  25. BIN
      images/theme/10006.jpg
  26. BIN
      images/theme/10007.jpg
  27. BIN
      images/unlock.png
  28. 347 0
      js/cache.js
  29. 36 0
      js/decrypt.js
  30. 36 0
      js/genKeys.js
  31. 45 0
      js/info.js
  32. 5382 0
      js/jsencrypt.js
  33. 34 0
      js/lists.js
  34. 157 0
      js/login.js
  35. 95 0
      js/rand.js
  36. 37 0
      js/remove.js
  37. 32 0
      js/resetKeys.js
  38. 35 0
      js/saveClientPublicKey.js
  39. 57 0
      js/saveKeys.js
  40. 75 0
      js/savePwd.js
  41. 32 0
      js/valid.js
  42. 28 0
      js/verifyKeys.js
  43. 83 0
      pages/components/about/about.js
  44. 4 0
      pages/components/about/about.json
  45. 34 0
      pages/components/about/about.wxml
  46. 33 0
      pages/components/about/about.wxss
  47. 25 0
      pages/components/background/background.js
  48. 4 0
      pages/components/background/background.json
  49. 3 0
      pages/components/background/background.wxml
  50. 17 0
      pages/components/background/background.wxss
  51. 631 0
      pages/index/index.js
  52. 6 0
      pages/index/index.json
  53. 84 0
      pages/index/index.wxml
  54. 50 0
      pages/index/index.wxss
  55. 125 0
      pages/keys/keys.js
  56. 5 0
      pages/keys/keys.json
  57. 26 0
      pages/keys/keys.wxml
  58. 1 0
      pages/keys/keys.wxss
  59. 219 0
      pages/password/password.js
  60. 5 0
      pages/password/password.json
  61. 44 0
      pages/password/password.wxml
  62. 1 0
      pages/password/password.wxss
  63. 142 0
      pages/privatekey/privatekey.js
  64. 5 0
      pages/privatekey/privatekey.json
  65. 27 0
      pages/privatekey/privatekey.wxml
  66. 1 0
      pages/privatekey/privatekey.wxss
  67. 167 0
      pages/reset/reset.js
  68. 5 0
      pages/reset/reset.json
  69. 27 0
      pages/reset/reset.wxml
  70. 1 0
      pages/reset/reset.wxss
  71. 55 0
      project.config.json
  72. 7 0
      sitemap.json
  73. 305 0
      utils/util.js

+ 5 - 0
README.txt

@@ -0,0 +1,5 @@
+Hi!大家好~欢迎使用密码本!
+
+该项目的背景是由于不久前邮箱密码被盗,被小偷将我的一个短域名转走,从此认识到密码的重要性,由于平时的密码设置为了方便记忆会将密码设置得短,密码强度不高,很容易被破解,设置过于复杂又不方便记忆,所以开发密码本来管理自己的密码,您可以通过它生成复杂的随机密码,也可以保存您自定义的密码。
+
+密码本的密码都是通过OpenSSL 256 位非对称加密,所以请您生成公私钥后,一定要保存好自己的私钥,防止私钥丢失导致密码泄露,如果您忘记了私钥,密码将会全部丢失。

+ 113 - 0
app.js

@@ -0,0 +1,113 @@
+//app.js
+const appConfig = require('config')
+App({
+  onLaunch: function () {
+    // 展示本地存储能力
+    // 登录
+    /*
+    wx.login({
+      success: res => {
+        this.log("wx.login", res);
+        // 发送 res.code 到后台换取 openId, sessionKey, unionId
+      } 
+    })
+    var _this = this;
+    wx.getNetworkType({
+      success(res) {
+        _this.globalData.networkType = res.networkType
+        console.log(_this.globalData.networkType);
+      },
+      complete : function(res){
+        console.log("complete", res);
+      }
+    })
+    wx.onNetworkStatusChange(function (res) {
+      console.log("res.isConnected", res.isConnected)
+      console.log("res.networkType", res.networkType)
+    })*/
+    
+    // 获取用户信息
+    this.login();
+  },
+  login: function(){
+    wx.getSetting({
+      success: res => {
+        if (res.authSetting['scope.userInfo']) {
+          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
+          wx.login({
+            success: res => {
+              if (res.code && res.code != "") {
+                this.globalData.code = res.code
+                wx.getUserInfo({
+                  success: res => {
+                    // 可以将 res 发送给后台解码出 unionId
+                    this.globalData.res = res;
+                    this.globalData.userInfo = res.userInfo
+                    // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
+                    // 所以此处加入 callback 以防止这种情况
+                    if (this.userInfoReadyCallback) {
+                      this.userInfoReadyCallback(res)
+                    }
+                  },
+                  fail: res => {
+                    this.warning("网络错误,请检查您的网络是否正常~");
+                  }
+                });
+              }else{
+                this.warning("登录失败,请重新授权后登录~");
+              }
+            }
+          });
+        } else {
+          //请先授权
+        }
+      }
+    });
+  },
+  globalData: {
+    code : null,
+    userInfo: null,
+    isReloadLists: false,
+    isReloadIcon: false,
+    info : null,
+    res : null,
+    background: "/images/theme/10001.jpg",
+    networkType : "wifi",
+  },
+  toast : function(str)  {
+    wx.showToast({
+      title: str,
+      duration: appConfig.duration
+    });
+  },
+  warning : function(str) {
+    wx.showModal({
+      title: "温馨提示",
+      content : str || "",
+      showCancel : false,
+    })
+  },
+  getStackTrace : function () {
+    var obj = {};
+    Error.captureStackTrace(obj, this.getStackTrace);
+    return obj.stack;
+  },
+  log : function() {
+    /*
+    if(appConfig.host.indexOf("http://localhost/") == -1) {
+      return;
+    }*/
+    //console.log.apply(console, arguments)
+    var stack = this.getStackTrace() || ""
+    var matchResult = stack.split("at");
+    
+    var line = matchResult[5] || ""
+    for (var i in arguments) {
+    }
+    if (typeof arguments[i] == 'object') {
+      arguments[i] = JSON.stringify(arguments[i])
+    }
+    arguments[i] += " " + line.replace("Object.", "").replace("(", "").replace(")", "");
+    console.log.apply(console, arguments)
+  },
+})

+ 19 - 0
app.json

@@ -0,0 +1,19 @@
+{
+  "pages": [
+    "pages/index/index",
+    "pages/password/password",
+    "pages/keys/keys",
+    "pages/reset/reset",
+    "pages/components/about/about",
+    "pages/components/background/background",
+    "pages/privatekey/privatekey"
+  ],
+  "window": {
+    "backgroundTextStyle": "light",
+    "navigationBarBackgroundColor": "#fff",
+    "navigationBarTitleText": "密码本",
+    "navigationBarTextStyle": "black",
+    "enablePullDownRefresh": true
+  },
+  "sitemapLocation": "sitemap.json"
+}

+ 153 - 0
app.wxss

@@ -0,0 +1,153 @@
+/**app.wxss**/
+@import "colorui/main.wxss";
+@import "colorui/icon.wxss";
+@import "pages/components/background/background.wxss";
+
+.container {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: space-between;
+  padding: 20rpx 0;
+  box-sizing: border-box;
+}
+.hidden {
+  display: none;
+}
+.show {
+  display: block;
+}
+.margin-10{
+  margin: 10rpx 10rpx;
+}
+.margin-20{
+  margin: 20rpx 20rpx;
+}
+.font-18{
+  font-size:1.2em;
+}
+.font-20{
+  font-size:1.5em;
+}
+.scrollPage {
+  height: 100vh;
+}
+
+.nav-list {
+  display: flex;
+  flex-wrap: wrap;
+  padding: 0px 40rpx 0px;
+  justify-content: space-between;
+}
+
+.nav-li {
+  padding: 30rpx;
+  border-radius: 12rpx;
+  width: 45%;
+  margin: 0 2.5% 40rpx;
+  background-image: url(https://image.weilanwl.com/color2.0/cardBg.png);
+  background-size: cover;
+  background-position: center;
+  position: relative;
+  z-index: 1;
+}
+
+.nav-li::after {
+  content: "";
+  position: absolute;
+  z-index: -1;
+  background-color: inherit;
+  width: 100%;
+  height: 100%;
+  left: 0;
+  bottom: -10%;
+  border-radius: 10rpx;
+  opacity: 0.2;
+  transform: scale(0.9, 0.9);
+}
+
+.nav-li.cur {
+  color: #fff;
+  background: rgb(94, 185, 94);
+  box-shadow: 4rpx 4rpx 6rpx rgba(94, 185, 94, 0.4);
+}
+
+.nav-title {
+  font-size: 32rpx;
+  font-weight: 300;
+}
+
+.nav-title::first-letter {
+  font-size: 40rpx;
+  margin-right: 4rpx;
+}
+
+.nav-name {
+  font-size: 28rpx;
+  text-transform: Capitalize;
+  margin-top: 20rpx;
+  position: relative;
+}
+
+.nav-name::before {
+  content: "";
+  position: absolute;
+  display: block;
+  width: 40rpx;
+  height: 6rpx;
+  background: #fff;
+  bottom: 0;
+  right: 0;
+  opacity: 0.5;
+}
+
+.nav-name::after {
+  content: "";
+  position: absolute;
+  display: block;
+  width: 100rpx;
+  height: 1px;
+  background: #fff;
+  bottom: 0;
+  right: 40rpx;
+  opacity: 0.3;
+}
+
+.nav-name::first-letter {
+  font-weight: bold;
+  font-size: 36rpx;
+  margin-right: 1px;
+}
+
+.nav-li text {
+  position: absolute;
+  right: 30rpx;
+  top: 30rpx;
+  font-size: 52rpx;
+  width: 60rpx;
+  height: 60rpx;
+  text-align: center;
+  line-height: 60rpx;
+}
+.text-light {
+  font-weight: 300;
+}
+.justify-middle{
+  margin: 0 auto;
+}
+.red {
+  color: red;
+}
+.padding-xl {
+	padding: 50rpx;
+}
+.cu-avatar.small-font{
+  font-size: 1.0em;
+}
+.padding-tabbar{
+   padding-bottom: 120rpx;
+ }
+ button[plain]{ border:0 }
+ .tab-bottom{padding:43rpx;}
+ .text-center{text-align: center;}

+ 184 - 0
colorui/animation.wxss

@@ -0,0 +1,184 @@
+/* 
+  Animation 微动画  
+  基于ColorUI组建库的动画模块 by 文晓港 2019年3月26日19:52:28
+ */
+
+/* css 滤镜 控制黑白底色gif的 */
+.gif-black{  
+  mix-blend-mode: screen;  
+}
+.gif-white{  
+  mix-blend-mode: multiply; 
+}
+
+
+/* Animation css */
+[class*=animation-] {
+    animation-duration: .5s;
+    animation-timing-function: ease-out;
+    animation-fill-mode: both
+}
+
+.animation-fade {
+    animation-name: fade;
+    animation-duration: .8s;
+    animation-timing-function: linear
+}
+
+.animation-scale-up {
+    animation-name: scale-up
+}
+
+.animation-scale-down {
+    animation-name: scale-down
+}
+
+.animation-slide-top {
+    animation-name: slide-top
+}
+
+.animation-slide-bottom {
+    animation-name: slide-bottom
+}
+
+.animation-slide-left {
+    animation-name: slide-left
+}
+
+.animation-slide-right {
+    animation-name: slide-right
+}
+
+.animation-shake {
+    animation-name: shake
+}
+
+.animation-reverse {
+    animation-direction: reverse
+}
+
+@keyframes fade {
+    0% {
+        opacity: 0
+    }
+
+    100% {
+        opacity: 1
+    }
+}
+
+@keyframes scale-up {
+    0% {
+        opacity: 0;
+        transform: scale(.2)
+    }
+
+    100% {
+        opacity: 1;
+        transform: scale(1)
+    }
+}
+
+@keyframes scale-down {
+    0% {
+        opacity: 0;
+        transform: scale(1.8)
+    }
+
+    100% {
+        opacity: 1;
+        transform: scale(1)
+    }
+}
+
+@keyframes slide-top {
+    0% {
+        opacity: 0;
+        transform: translateY(-100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateY(0)
+    }
+}
+
+@keyframes slide-bottom {
+    0% {
+        opacity: 0;
+        transform: translateY(100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateY(0)
+    }
+}
+
+@keyframes shake {
+
+    0%,
+    100% {
+        transform: translateX(0)
+    }
+
+    10% {
+        transform: translateX(-9px)
+    }
+
+    20% {
+        transform: translateX(8px)
+    }
+
+    30% {
+        transform: translateX(-7px)
+    }
+
+    40% {
+        transform: translateX(6px)
+    }
+
+    50% {
+        transform: translateX(-5px)
+    }
+
+    60% {
+        transform: translateX(4px)
+    }
+
+    70% {
+        transform: translateX(-3px)
+    }
+
+    80% {
+        transform: translateX(2px)
+    }
+
+    90% {
+        transform: translateX(-1px)
+    }
+}
+
+@keyframes slide-left {
+    0% {
+        opacity: 0;
+        transform: translateX(-100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateX(0)
+    }
+}
+
+@keyframes slide-right {
+    0% {
+        opacity: 0;
+        transform: translateX(100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateX(0)
+    }
+}

+ 54 - 0
colorui/components/cu-custom.js

@@ -0,0 +1,54 @@
+const app = getApp();
+Component({
+  /**
+   * 组件的一些选项
+   */
+  options: {
+    addGlobalClass: true,
+    multipleSlots: true
+  },
+  /**
+   * 组件的对外属性
+   */
+  properties: {
+    bgColor: {
+      type: String,
+      default: ''
+    }, 
+    isCustom: {
+      type: [Boolean, String],
+      default: false
+    },
+    isBack: {
+      type: [Boolean, String],
+      default: false
+    },
+    bgImage: {
+      type: String,
+      default: ''
+    },
+  },
+  /**
+   * 组件的初始数据
+   */
+  data: {
+    StatusBar: app.globalData.StatusBar,
+    CustomBar: app.globalData.CustomBar,
+    Custom: app.globalData.Custom
+  },
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+    BackPage() {
+      wx.navigateBack({
+        delta: 1
+      });
+    },
+    toHome(){
+      wx.reLaunch({
+        url: '/pages/index/index',
+      })
+    }
+  }
+})

+ 4 - 0
colorui/components/cu-custom.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 16 - 0
colorui/components/cu-custom.wxml

@@ -0,0 +1,16 @@
+<view class="cu-custom" style="height:{{CustomBar}}px">
+  <view class="cu-bar fixed {{bgImage!=''?'none-bg text-white bg-img':''}} {{bgColor}}" style="height:{{CustomBar}}px;padding-top:{{StatusBar}}px;{{bgImage?'background-image:url(' + bgImage+')':''}}">
+    <view class="action" bindtap="BackPage" wx:if="{{isBack}}">
+      <text class="cuIcon-back"></text>
+      <slot name="backText"></slot>
+    </view>
+    <view class="action border-custom"  wx:if="{{isCustom}}" style="width:{{Custom.width}}px;height:{{Custom.height}}px;margin-left:calc(750rpx - {{Custom.right}}px)">
+      <text class="cuIcon-back" bindtap="BackPage"></text>
+      <text class="cuIcon-homefill" bindtap="toHome"></text>
+    </view>
+    <view class="content" style="top:{{StatusBar}}px">
+      <slot name="content"></slot>
+    </view>
+    <slot name="right"></slot>
+  </view>
+</view>

+ 1 - 0
colorui/components/cu-custom.wxss

@@ -0,0 +1 @@
+/* colorui/components/cu-custom.wxss */

File diff suppressed because it is too large
+ 36 - 0
colorui/icon.wxss


+ 3941 - 0
colorui/main.wxss

@@ -0,0 +1,3941 @@
+/*
+  ColorUi for MP-weixin  v2.1.4 | by 文晓港 2019年4月25日19:15:42
+  仅供学习交流,如作它用所承受的法律责任一概与作者无关
+  使用ColorUi开发扩展与插件时,请注明基于ColorUi开发
+  
+  (QQ交流群:240787041)
+*/
+
+/* ==================
+        初始化
+ ==================== */
+page {
+	/* Color 可以自定义相关配色 */
+	/* var属性兼容性 --> https://www.caniuse.com/#feat=css-variables */
+	/* 标准色 */
+	--red: #e54d42;
+	--orange: #f37b1d;
+	--yellow: #fbbd08;
+	--olive: #8dc63f;
+	--green: #39b54a;
+	--cyan: #1cbbb4;
+	--blue: #0081ff;
+	--purple: #6739b6;
+	--mauve: #9c26b0;
+	--pink: #e03997;
+	--brown: #a5673f;
+	--grey: #8799a3;
+	--black: #333333;
+	--darkGray: #666666;
+	--gray: #aaaaaa;
+	--ghostWhite: #f1f1f1;
+	--white: #ffffff;
+	/* 浅色 */
+	--redLight: #fadbd9;
+	--orangeLight: #fde6d2;
+	--yellowLight: #fef2ce;
+	--oliveLight: #e8f4d9;
+	--greenLight: #d7f0db;
+	--cyanLight: #d2f1f0;
+	--blueLight: #cce6ff;
+	--purpleLight: #e1d7f0;
+	--mauveLight: #ebd4ef;
+	--pinkLight: #f9d7ea;
+	--brownLight: #ede1d9;
+	--greyLight: #e7ebed;
+	/* 渐变色 */
+	--gradualRed: linear-gradient(45deg, #f43f3b, #ec008c);
+	--gradualOrange: linear-gradient(45deg, #ff9700, #ed1c24);
+	--gradualGreen: linear-gradient(45deg, #39b54a, #8dc63f);
+	--gradualPurple: linear-gradient(45deg, #9000ff, #5e00ff);
+	--gradualPink: linear-gradient(45deg, #ec008c, #6739b6);
+	--gradualBlue: linear-gradient(45deg, #0081ff, #1cbbb4);
+	/* 阴影透明色 */
+	--ShadowSize: 6rpx 6rpx 8rpx;
+	--redShadow: rgba(204, 69, 59, 0.2);
+	--orangeShadow: rgba(217, 109, 26, 0.2);
+	--yellowShadow: rgba(224, 170, 7, 0.2);
+	--oliveShadow: rgba(124, 173, 55, 0.2);
+	--greenShadow: rgba(48, 156, 63, 0.2);
+	--cyanShadow: rgba(28, 187, 180, 0.2);
+	--blueShadow: rgba(0, 102, 204, 0.2);
+	--purpleShadow: rgba(88, 48, 156, 0.2);
+	--mauveShadow: rgba(133, 33, 150, 0.2);
+	--pinkShadow: rgba(199, 50, 134, 0.2);
+	--brownShadow: rgba(140, 88, 53, 0.2);
+	--greyShadow: rgba(114, 130, 138, 0.2);
+	--grayShadow: rgba(114, 130, 138, 0.2);
+	--blackShadow: rgba(26, 26, 26, 0.2);
+
+	background-color: var(--ghostWhite);
+	font-size: 28rpx;
+	color: var(--black);
+	font-family: Helvetica Neue, Helvetica, sans-serif;
+}
+
+view,
+scroll-view,
+swiper,
+button,
+input,
+textarea,
+label,
+navigator,
+image {
+	box-sizing: border-box;
+}
+
+.round {
+	border-radius: 5000rpx;
+}
+
+.radius {
+	border-radius: 6rpx;
+}
+
+/* ==================
+          图片
+ ==================== */
+
+image {
+	max-width: 100%;
+	display: inline-block;
+	position: relative;
+	z-index: 0;
+}
+
+image.loading::before {
+	content: "";
+	background-color: #f5f5f5;
+	display: block;
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	z-index: -2;
+}
+
+image.loading::after {
+	content: "\e7f1";
+	font-family: "cuIcon";
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 32rpx;
+	height: 32rpx;
+	line-height: 32rpx;
+	right: 0;
+	bottom: 0;
+	z-index: -1;
+	font-size: 32rpx;
+	margin: auto;
+	color: #ccc;
+	-webkit-animation: cuIcon-spin 2s infinite linear;
+	animation: cuIcon-spin 2s infinite linear;
+	display: block;
+}
+
+.response {
+	width: 100%;
+}
+
+/* ==================
+         开关
+ ==================== */
+
+switch,
+checkbox,
+radio {
+	position: relative;
+}
+
+switch::after,
+switch::before {
+	font-family: "cuIcon";
+	content: "\e645";
+	position: absolute;
+	color: var(--white) !important;
+	top: 0%;
+	left: 0rpx;
+	font-size: 26rpx;
+	line-height: 26px;
+	width: 50%;
+	text-align: center;
+	pointer-events: none;
+	transform: scale(0, 0);
+	transition: all 0.3s ease-in-out 0s;
+	z-index: 9;
+	bottom: 0;
+	height: 26px;
+	margin: auto;
+}
+
+switch::before {
+	content: "\e646";
+	right: 0;
+	transform: scale(1, 1);
+	left: auto;
+}
+
+switch[checked]::after,
+switch.checked::after {
+	transform: scale(1, 1);
+}
+
+switch[checked]::before,
+switch.checked::before {
+	transform: scale(0, 0);
+}
+
+switch[checked]::before {
+	transform: scale(0, 0);
+}
+
+radio::before,
+checkbox::before {
+	font-family: "cuIcon";
+	content: "\e645";
+	position: absolute;
+	color: var(--white) !important;
+	top: 50%;
+	margin-top: -8px;
+	right: 5px;
+	font-size: 32rpx;
+	line-height: 16px;
+	pointer-events: none;
+	transform: scale(1, 1);
+	transition: all 0.3s ease-in-out 0s;
+	z-index: 9;
+}
+
+radio .wx-radio-input,
+checkbox .wx-checkbox-input {
+	margin: 0;
+	width: 24px;
+	height: 24px;
+}
+
+checkbox.round .wx-checkbox-input {
+	border-radius: 100rpx;
+}
+
+switch .wx-switch-input {
+	border: none;
+	padding: 0 24px;
+	width: 48px;
+	height: 26px;
+	margin: 0;
+	border-radius: 100rpx;
+}
+
+switch .wx-switch-input:not([class*="bg-"]) {
+	background: var(--grey) !important;
+}
+
+switch .wx-switch-input::after {
+	margin: auto;
+	width: 26px;
+	height: 26px;
+	border-radius: 100rpx;
+	left: 0rpx;
+	top: 0rpx;
+	bottom: 0rpx;
+	position: absolute;
+	transform: scale(0.9, 0.9);
+	transition: all 0.1s ease-in-out 0s;
+}
+
+switch .wx-switch-input.wx-switch-input-checked::after {
+	margin: auto;
+	left: 22px;
+	box-shadow: none;
+	transform: scale(0.9, 0.9);
+}
+
+radio-group {
+	display: inline-block;
+}
+
+
+
+switch.radius .wx-switch-input::after,
+switch.radius .wx-switch-input,
+switch.radius .wx-switch-input::before {
+	border-radius: 10rpx;
+}
+
+switch .wx-switch-input::before,
+radio.radio::before,
+checkbox .wx-checkbox-input::before,
+radio .wx-radio-input::before,
+radio.radio::before {
+	display: none;
+}
+
+radio.radio[checked]::after {
+	content: "";
+	background-color: transparent;
+	display: block;
+	position: absolute;
+	width: 8px;
+	height: 8px;
+	z-index: 999;
+	top: 0rpx;
+	left: 0rpx;
+	right: 0;
+	bottom: 0;
+	margin: auto;
+	border-radius: 200rpx;
+	border: 8px solid var(--white) !important;
+}
+
+.switch-sex::after {
+	content: "\e71c";
+}
+
+.switch-sex::before {
+	content: "\e71a";
+}
+
+.switch-sex .wx-switch-input {
+	background: var(--red) !important;
+	border-color: var(--red) !important;
+}
+
+.switch-sex[checked] .wx-switch-input {
+	background: var(--blue) !important;
+	border-color: var(--blue) !important;
+}
+
+switch.red[checked] .wx-switch-input,
+checkbox.red[checked] .wx-checkbox-input,
+radio.red[checked] .wx-radio-input {
+	border-color: var(--red) !important;
+}
+
+switch.orange[checked] .wx-switch-input,
+checkbox.orange[checked] .wx-checkbox-input,
+radio.orange[checked] .wx-radio-input {
+	border-color: var(--orange) !important;
+}
+
+switch.yellow[checked] .wx-switch-input,
+checkbox.yellow[checked] .wx-checkbox-input,
+radio.yellow[checked] .wx-radio-input {
+	border-color: var(--yellow) !important;
+}
+
+switch.olive[checked] .wx-switch-input,
+checkbox.olive[checked] .wx-checkbox-input,
+radio.olive[checked] .wx-radio-input {
+	border-color: var(--olive) !important;
+}
+
+switch.green[checked] .wx-switch-input,
+checkbox.green[checked] .wx-checkbox-input,
+checkbox[checked] .wx-checkbox-input,
+radio.green[checked] .wx-radio-input {
+	border-color: var(--green) !important;
+}
+
+switch.cyan[checked] .wx-switch-input,
+checkbox.cyan[checked] .wx-checkbox-input,
+radio.cyan[checked] .wx-radio-input {
+	border-color: var(--cyan) !important;
+}
+
+switch.blue[checked] .wx-switch-input,
+checkbox.blue[checked] .wx-checkbox-input,
+radio.blue[checked] .wx-radio-input {
+	border-color: var(--blue) !important;
+}
+
+switch.purple[checked] .wx-switch-input,
+checkbox.purple[checked] .wx-checkbox-input,
+radio.purple[checked] .wx-radio-input {
+	border-color: var(--purple) !important;
+}
+
+switch.mauve[checked] .wx-switch-input,
+checkbox.mauve[checked] .wx-checkbox-input,
+radio.mauve[checked] .wx-radio-input {
+	border-color: var(--mauve) !important;
+}
+
+switch.pink[checked] .wx-switch-input,
+checkbox.pink[checked] .wx-checkbox-input,
+radio.pink[checked] .wx-radio-input {
+	border-color: var(--pink) !important;
+}
+
+switch.brown[checked] .wx-switch-input,
+checkbox.brown[checked] .wx-checkbox-input,
+radio.brown[checked] .wx-radio-input {
+	border-color: var(--brown) !important;
+}
+
+switch.grey[checked] .wx-switch-input,
+checkbox.grey[checked] .wx-checkbox-input,
+radio.grey[checked] .wx-radio-input {
+	border-color: var(--grey) !important;
+}
+
+switch.gray[checked] .wx-switch-input,
+checkbox.gray[checked] .wx-checkbox-input,
+radio.gray[checked] .wx-radio-input {
+	border-color: var(--grey) !important;
+}
+
+switch.black[checked] .wx-switch-input,
+checkbox.black[checked] .wx-checkbox-input,
+radio.black[checked] .wx-radio-input {
+	border-color: var(--black) !important;
+}
+
+switch.white[checked] .wx-switch-input,
+checkbox.white[checked] .wx-checkbox-input,
+radio.white[checked] .wx-radio-input {
+	border-color: var(--white) !important;
+}
+
+switch.red[checked] .wx-switch-input.wx-switch-input-checked,
+checkbox.red[checked] .wx-checkbox-input,
+radio.red[checked] .wx-radio-input {
+	background-color: var(--red) !important;
+	color: var(--white) !important;
+}
+
+switch.orange[checked] .wx-switch-input,
+checkbox.orange[checked] .wx-checkbox-input,
+radio.orange[checked] .wx-radio-input {
+	background-color: var(--orange) !important;
+	color: var(--white) !important;
+}
+
+switch.yellow[checked] .wx-switch-input,
+checkbox.yellow[checked] .wx-checkbox-input,
+radio.yellow[checked] .wx-radio-input {
+	background-color: var(--yellow) !important;
+	color: var(--black) !important;
+}
+
+switch.olive[checked] .wx-switch-input,
+checkbox.olive[checked] .wx-checkbox-input,
+radio.olive[checked] .wx-radio-input {
+	background-color: var(--olive) !important;
+	color: var(--white) !important;
+}
+
+switch.green[checked] .wx-switch-input,
+switch[checked] .wx-switch-input,
+checkbox.green[checked] .wx-checkbox-input,
+checkbox[checked] .wx-checkbox-input,
+radio.green[checked] .wx-radio-input,
+radio[checked] .wx-radio-input {
+	background-color: var(--green) !important;
+	color: var(--white) !important;
+}
+
+switch.cyan[checked] .wx-switch-input,
+checkbox.cyan[checked] .wx-checkbox-input,
+radio.cyan[checked] .wx-radio-input {
+	background-color: var(--cyan) !important;
+	color: var(--white) !important;
+}
+
+switch.blue[checked] .wx-switch-input,
+checkbox.blue[checked] .wx-checkbox-input,
+radio.blue[checked] .wx-radio-input {
+	background-color: var(--blue) !important;
+	color: var(--white) !important;
+}
+
+switch.purple[checked] .wx-switch-input,
+checkbox.purple[checked] .wx-checkbox-input,
+radio.purple[checked] .wx-radio-input {
+	background-color: var(--purple) !important;
+	color: var(--white) !important;
+}
+
+switch.mauve[checked] .wx-switch-input,
+checkbox.mauve[checked] .wx-checkbox-input,
+radio.mauve[checked] .wx-radio-input {
+	background-color: var(--mauve) !important;
+	color: var(--white) !important;
+}
+
+switch.pink[checked] .wx-switch-input,
+checkbox.pink[checked] .wx-checkbox-input,
+radio.pink[checked] .wx-radio-input {
+	background-color: var(--pink) !important;
+	color: var(--white) !important;
+}
+
+switch.brown[checked] .wx-switch-input,
+checkbox.brown[checked] .wx-checkbox-input,
+radio.brown[checked] .wx-radio-input {
+	background-color: var(--brown) !important;
+	color: var(--white) !important;
+}
+
+switch.grey[checked] .wx-switch-input,
+checkbox.grey[checked] .wx-checkbox-input,
+radio.grey[checked] .wx-radio-input {
+	background-color: var(--grey) !important;
+	color: var(--white) !important;
+}
+
+switch.gray[checked] .wx-switch-input,
+checkbox.gray[checked] .wx-checkbox-input,
+radio.gray[checked] .wx-radio-input {
+	background-color: #f0f0f0 !important;
+	color: var(--black) !important;
+}
+
+switch.black[checked] .wx-switch-input,
+checkbox.black[checked] .wx-checkbox-input,
+radio.black[checked] .wx-radio-input {
+	background-color: var(--black) !important;
+	color: var(--white) !important;
+}
+
+switch.white[checked] .wx-switch-input,
+checkbox.white[checked] .wx-checkbox-input,
+radio.white[checked] .wx-radio-input {
+	background-color: var(--white) !important;
+	color: var(--black) !important;
+}
+
+/* ==================
+          边框
+ ==================== */
+
+/* -- 实线 -- */
+
+.solid,
+.solid-top,
+.solid-right,
+.solid-bottom,
+.solid-left,
+.solids,
+.solids-top,
+.solids-right,
+.solids-bottom,
+.solids-left,
+.dashed,
+.dashed-top,
+.dashed-right,
+.dashed-bottom,
+.dashed-left {
+	position: relative;
+}
+
+.solid::after,
+.solid-top::after,
+.solid-right::after,
+.solid-bottom::after,
+.solid-left::after,
+.solids::after,
+.solids-top::after,
+.solids-right::after,
+.solids-bottom::after,
+.solids-left::after,
+.dashed::after,
+.dashed-top::after,
+.dashed-right::after,
+.dashed-bottom::after,
+.dashed-left::after {
+	content: " ";
+	width: 200%;
+	height: 200%;
+	position: absolute;
+	top: 0;
+	left: 0;
+	border-radius: inherit;
+	transform: scale(0.5);
+	transform-origin: 0 0;
+	pointer-events: none;
+	box-sizing: border-box;
+}
+
+.solid::after {
+	border: 1rpx solid rgba(0, 0, 0, 0.1);
+}
+
+.solid-top::after {
+	border-top: 1rpx solid rgba(0, 0, 0, 0.1);
+}
+
+.solid-right::after {
+	border-right: 1rpx solid rgba(0, 0, 0, 0.1);
+}
+
+.solid-bottom::after {
+	border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
+}
+
+.solid-left::after {
+	border-left: 1rpx solid rgba(0, 0, 0, 0.1);
+}
+
+.solids::after {
+	border: 8rpx solid #eee;
+}
+
+.solids-top::after {
+	border-top: 8rpx solid #eee;
+}
+
+.solids-right::after {
+	border-right: 8rpx solid #eee;
+}
+
+.solids-bottom::after {
+	border-bottom: 8rpx solid #eee;
+}
+
+.solids-left::after {
+	border-left: 8rpx solid #eee;
+}
+
+/* -- 虚线 -- */
+
+.dashed::after {
+	border: 1rpx dashed #ddd;
+}
+
+.dashed-top::after {
+	border-top: 1rpx dashed #ddd;
+}
+
+.dashed-right::after {
+	border-right: 1rpx dashed #ddd;
+}
+
+.dashed-bottom::after {
+	border-bottom: 1rpx dashed #ddd;
+}
+
+.dashed-left::after {
+	border-left: 1rpx dashed #ddd;
+}
+
+/* -- 阴影 -- */
+
+.shadow[class*='white'] {
+	--ShadowSize: 0 1rpx 6rpx;
+}
+
+.shadow-lg {
+	--ShadowSize: 0rpx 40rpx 100rpx 0rpx;
+}
+
+.shadow-warp {
+	position: relative;
+	box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
+}
+
+.shadow-warp:before,
+.shadow-warp:after {
+	position: absolute;
+	content: "";
+	top: 20rpx;
+	bottom: 30rpx;
+	left: 20rpx;
+	width: 50%;
+	box-shadow: 0 30rpx 20rpx rgba(0, 0, 0, 0.2);
+	transform: rotate(-3deg);
+	z-index: -1;
+}
+
+.shadow-warp:after {
+	right: 20rpx;
+	left: auto;
+	transform: rotate(3deg);
+}
+
+.shadow-blur {
+	position: relative;
+}
+
+.shadow-blur::before {
+	content: "";
+	display: block;
+	background: inherit;
+	filter: blur(10rpx);
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	top: 10rpx;
+	left: 10rpx;
+	z-index: -1;
+	opacity: 0.4;
+	transform-origin: 0 0;
+	border-radius: inherit;
+	transform: scale(1, 1);
+}
+
+/* ==================
+          按钮
+ ==================== */
+
+.cu-btn {
+	position: relative;
+	border: 0rpx;
+	display: inline-flex;
+	align-items: center;
+	justify-content: center;
+	box-sizing: border-box;
+	padding: 0 30rpx;
+	font-size: 28rpx;
+	height: 64rpx;
+	line-height: 1;
+	text-align: center;
+	text-decoration: none;
+	overflow: visible;
+	margin-left: initial;
+	transform: translate(0rpx, 0rpx);
+	margin-right: initial;
+}
+
+.cu-btn::after {
+	display: none;
+}
+
+.cu-btn:not([class*="bg-"]) {
+	background-color: #f0f0f0;
+}
+
+.cu-btn[class*="line"] {
+	background-color: transparent;
+}
+
+.cu-btn[class*="line"]::after {
+	content: " ";
+	display: block;
+	width: 200%;
+	height: 200%;
+	position: absolute;
+	top: 0;
+	left: 0;
+	border: 1rpx solid currentColor;
+	transform: scale(0.5);
+	transform-origin: 0 0;
+	box-sizing: border-box;
+	border-radius: 12rpx;
+	z-index: 1;
+	pointer-events: none;
+}
+
+.cu-btn.round[class*="line"]::after {
+	border-radius: 1000rpx;
+}
+
+.cu-btn[class*="lines"]::after {
+	border: 6rpx solid currentColor;
+}
+
+.cu-btn[class*="bg-"]::after {
+	display: none;
+}
+
+.cu-btn.sm {
+	padding: 0 20rpx;
+	font-size: 20rpx;
+	height: 48rpx;
+}
+
+.cu-btn.lg {
+	padding: 0 40rpx;
+	font-size: 32rpx;
+	height: 80rpx;
+}
+
+.cu-btn.icon.sm {
+	width: 48rpx;
+	height: 48rpx;
+}
+
+.cu-btn.icon {
+	width: 64rpx;
+	height: 64rpx;
+	border-radius: 500rpx;
+	padding: 0;
+}
+
+button.icon.lg {
+	width: 80rpx;
+	height: 80rpx;
+}
+
+.cu-btn.shadow-blur::before {
+	top: 4rpx;
+	left: 4rpx;
+	filter: blur(6rpx);
+	opacity: 0.6;
+}
+
+.cu-btn.button-hover {
+	transform: translate(1rpx, 1rpx);
+}
+
+.block {
+	display: block;
+}
+
+.cu-btn.block {
+	display: flex;
+}
+
+.cu-btn[disabled] {
+	opacity: 0.6;
+	color: var(--white);
+}
+
+/* ==================
+          徽章
+ ==================== */
+
+.cu-tag {
+	font-size: 24rpx;
+	vertical-align: middle;
+	position: relative;
+	display: inline-flex;
+	align-items: center;
+	justify-content: center;
+	box-sizing: border-box;
+	padding: 0rpx 16rpx;
+	height: 48rpx;
+	font-family: Helvetica Neue, Helvetica, sans-serif;
+	white-space: nowrap;
+}
+
+.cu-tag:not([class*="bg"]):not([class*="line"]) {
+	background-color: var(--ghostWhite);
+}
+
+.cu-tag[class*="line-"]::after {
+	content: " ";
+	width: 200%;
+	height: 200%;
+	position: absolute;
+	top: 0;
+	left: 0;
+	border: 1rpx solid currentColor;
+	transform: scale(0.5);
+	transform-origin: 0 0;
+	box-sizing: border-box;
+	border-radius: inherit;
+	z-index: 1;
+	pointer-events: none;
+}
+
+.cu-tag.radius[class*="line"]::after {
+	border-radius: 12rpx;
+}
+
+.cu-tag.round[class*="line"]::after {
+	border-radius: 1000rpx;
+}
+
+.cu-tag[class*="line-"]::after {
+	border-radius: 0;
+}
+
+.cu-tag+.cu-tag {
+	margin-left: 10rpx;
+}
+
+.cu-tag.sm {
+	font-size: 20rpx;
+	padding: 0rpx 12rpx;
+	height: 32rpx;
+}
+
+.cu-capsule {
+	display: inline-flex;
+	vertical-align: middle;
+}
+
+.cu-capsule+.cu-capsule {
+	margin-left: 10rpx;
+}
+
+.cu-capsule .cu-tag {
+	margin: 0;
+}
+
+.cu-capsule .cu-tag[class*="line-"]:last-child::after {
+	border-left: 0rpx solid transparent;
+}
+
+.cu-capsule .cu-tag[class*="line-"]:first-child::after {
+	border-right: 0rpx solid transparent;
+}
+
+.cu-capsule.radius .cu-tag:first-child {
+	border-top-left-radius: 6rpx;
+	border-bottom-left-radius: 6rpx;
+}
+
+.cu-capsule.radius .cu-tag:last-child::after,
+.cu-capsule.radius .cu-tag[class*="line-"] {
+	border-top-right-radius: 12rpx;
+	border-bottom-right-radius: 12rpx;
+}
+
+.cu-capsule.round .cu-tag:first-child {
+	border-top-left-radius: 200rpx;
+	border-bottom-left-radius: 200rpx;
+	text-indent: 4rpx;
+}
+
+.cu-capsule.round .cu-tag:last-child::after,
+.cu-capsule.round .cu-tag:last-child {
+	border-top-right-radius: 200rpx;
+	border-bottom-right-radius: 200rpx;
+	text-indent: -4rpx;
+}
+
+.cu-tag.badge {
+	border-radius: 200rpx;
+	position: absolute;
+	top: -10rpx;
+	right: -10rpx;
+	font-size: 20rpx;
+	padding: 0rpx 10rpx;
+	height: 28rpx;
+	color: var(--white);
+}
+
+.cu-tag.badge:not([class*="bg-"]) {
+	background-color: #dd514c;
+}
+
+.cu-tag:empty:not([class*="cuIcon-"]) {
+	padding: 0rpx;
+	width: 16rpx;
+	height: 16rpx;
+	top: -4rpx;
+	right: -4rpx;
+}
+
+.cu-tag[class*="cuIcon-"] {
+	width: 32rpx;
+	height: 32rpx;
+	top: -4rpx;
+	right: -4rpx;
+}
+
+/* ==================
+          头像
+ ==================== */
+
+.cu-avatar {
+	font-variant: small-caps;
+	margin: 0;
+	padding: 0;
+	display: inline-flex;
+	text-align: center;
+	justify-content: center;
+	align-items: center;
+	background-color: #ccc;
+	color: var(--white);
+	white-space: nowrap;
+	position: relative;
+	width: 64rpx;
+	height: 64rpx;
+	background-size: cover;
+	background-position: center;
+	vertical-align: middle;
+	font-size: 1.5em;
+}
+
+.cu-avatar.sm {
+	width: 48rpx;
+	height: 48rpx;
+	font-size: 1em;
+}
+
+.cu-avatar.lg {
+	width: 96rpx;
+	height: 96rpx;
+	font-size: 2em;
+}
+
+.cu-avatar.xl {
+	width: 128rpx;
+	height: 128rpx;
+	font-size: 2.5em;
+}
+
+.cu-avatar .avatar-text {
+	font-size: 0.4em;
+}
+
+.cu-avatar-group {
+	direction: rtl;
+	unicode-bidi: bidi-override;
+	padding: 0 10rpx 0 40rpx;
+	display: inline-block;
+}
+
+.cu-avatar-group .cu-avatar {
+	margin-left: -30rpx;
+	border: 4rpx solid var(--ghostWhite);
+	vertical-align: middle;
+}
+
+.cu-avatar-group .cu-avatar.sm {
+	margin-left: -20rpx;
+	border: 1rpx solid var(--ghostWhite);
+}
+
+/* ==================
+         进度条
+ ==================== */
+
+.cu-progress {
+	overflow: hidden;
+	height: 28rpx;
+	background-color: #ebeef5;
+	display: inline-flex;
+	align-items: center;
+	width: 100%;
+}
+
+.cu-progress+view,
+.cu-progress+text {
+	line-height: 1;
+}
+
+.cu-progress.xs {
+	height: 10rpx;
+}
+
+.cu-progress.sm {
+	height: 20rpx;
+}
+
+.cu-progress view {
+	width: 0;
+	height: 100%;
+	align-items: center;
+	display: flex;
+	justify-items: flex-end;
+	justify-content: space-around;
+	font-size: 20rpx;
+	color: var(--white);
+	transition: width 0.6s ease;
+}
+
+.cu-progress text {
+	align-items: center;
+	display: flex;
+	font-size: 20rpx;
+	color: var(--black);
+	text-indent: 10rpx;
+}
+
+.cu-progress.text-progress {
+	padding-right: 60rpx;
+}
+
+.cu-progress.striped view {
+	background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+	background-size: 72rpx 72rpx;
+}
+
+.cu-progress.active view {
+	animation: progress-stripes 2s linear infinite;
+}
+
+@keyframes progress-stripes {
+	from {
+		background-position: 72rpx 0;
+	}
+
+	to {
+		background-position: 0 0;
+	}
+}
+
+/* ==================
+          加载
+ ==================== */
+
+.cu-load {
+	display: block;
+	line-height: 3em;
+	text-align: center;
+}
+
+.cu-load::before {
+	font-family: "cuIcon";
+	display: inline-block;
+	margin-right: 6rpx;
+}
+
+.cu-load.loading::before {
+	content: "\e67a";
+	animation: cuIcon-spin 2s infinite linear;
+}
+
+.cu-load.loading::after {
+	content: "加载中...";
+}
+
+.cu-load.over::before {
+	content: "\e64a";
+}
+
+.cu-load.over::after {
+	content: "没有更多了";
+}
+
+.cu-load.erro::before {
+	content: "\e658";
+}
+
+.cu-load.erro::after {
+	content: "加载失败";
+}
+
+.cu-load.load-icon::before {
+	font-size: 32rpx;
+}
+
+.cu-load.load-icon::after {
+	display: none;
+}
+
+.cu-load.load-icon.over {
+	display: none;
+}
+
+.cu-load.load-modal {
+	position: fixed;
+	top: 0;
+	right: 0;
+	bottom: 140rpx;
+	left: 0;
+	margin: auto;
+	width: 260rpx;
+	height: 260rpx;
+	background-color: var(--white);
+	border-radius: 10rpx;
+	box-shadow: 0 0 0rpx 2000rpx rgba(0, 0, 0, 0.5);
+	display: flex;
+	align-items: center;
+	flex-direction: column;
+	justify-content: center;
+	font-size: 28rpx;
+	z-index: 9999;
+	line-height: 2.4em;
+}
+
+.cu-load.load-modal [class*="cuIcon-"] {
+	font-size: 60rpx;
+}
+
+.cu-load.load-modal image {
+	width: 70rpx;
+	height: 70rpx;
+}
+
+.cu-load.load-modal::after {
+	content: "";
+	position: absolute;
+	background-color: var(--white);
+	border-radius: 50%;
+	width: 200rpx;
+	height: 200rpx;
+	font-size: 10px;
+	border-top: 6rpx solid rgba(0, 0, 0, 0.05);
+	border-right: 6rpx solid rgba(0, 0, 0, 0.05);
+	border-bottom: 6rpx solid rgba(0, 0, 0, 0.05);
+	border-left: 6rpx solid var(--orange);
+	animation: cuIcon-spin 1s infinite linear;
+	z-index: -1;
+}
+
+.load-progress {
+	pointer-events: none;
+	top: 0;
+	position: fixed;
+	width: 100%;
+	left: 0;
+	z-index: 2000;
+}
+
+.load-progress.hide {
+	display: none;
+}
+
+.load-progress .load-progress-bar {
+	position: relative;
+	width: 100%;
+	height: 4rpx;
+	overflow: hidden;
+	transition: all 200ms ease 0s;
+}
+
+.load-progress .load-progress-spinner {
+	position: absolute;
+	top: 10rpx;
+	right: 10rpx;
+	z-index: 2000;
+	display: block;
+}
+
+.load-progress .load-progress-spinner::after {
+	content: "";
+	display: block;
+	width: 24rpx;
+	height: 24rpx;
+	-webkit-box-sizing: border-box;
+	box-sizing: border-box;
+	border: solid 4rpx transparent;
+	border-top-color: inherit;
+	border-left-color: inherit;
+	border-radius: 50%;
+	-webkit-animation: load-progress-spinner 0.4s linear infinite;
+	animation: load-progress-spinner 0.4s linear infinite;
+}
+
+@-webkit-keyframes load-progress-spinner {
+	0% {
+		-webkit-transform: rotate(0);
+		transform: rotate(0);
+	}
+
+	100% {
+		-webkit-transform: rotate(360deg);
+		transform: rotate(360deg);
+	}
+}
+
+@keyframes load-progress-spinner {
+	0% {
+		-webkit-transform: rotate(0);
+		transform: rotate(0);
+	}
+
+	100% {
+		-webkit-transform: rotate(360deg);
+		transform: rotate(360deg);
+	}
+}
+
+/* ==================
+          列表
+ ==================== */
+.grayscale {
+	filter: grayscale(1);
+}
+
+.cu-list+.cu-list {
+	margin-top: 30rpx
+}
+
+.cu-list>.cu-item {
+	transition: all .6s ease-in-out 0s;
+	transform: translateX(0rpx)
+}
+
+.cu-list>.cu-item.move-cur {
+	transform: translateX(-260rpx)
+}
+
+.cu-list>.cu-item .move {
+	position: absolute;
+	right: 0;
+	display: flex;
+	width: 260rpx;
+	height: 100%;
+	transform: translateX(100%)
+}
+
+.cu-list>.cu-item .move view {
+	display: flex;
+	flex: 1;
+	justify-content: center;
+	align-items: center
+}
+
+.cu-list.menu-avatar {
+	overflow: hidden;
+}
+
+.cu-list.menu-avatar>.cu-item {
+	position: relative;
+	display: flex;
+	padding-right: 10rpx;
+	height: 140rpx;
+	background-color: var(--white);
+	justify-content: flex-end;
+	align-items: center
+}
+
+.cu-list.menu-avatar>.cu-item>.cu-avatar {
+	position: absolute;
+	left: 30rpx
+}
+
+.cu-list.menu-avatar>.cu-item .flex .text-cut {
+	max-width: 510rpx
+}
+
+.cu-list.menu-avatar>.cu-item .content {
+	position: absolute;
+	left: 146rpx;
+	width: calc(100% - 96rpx - 60rpx - 120rpx - 20rpx);
+	line-height: 1.6em;
+}
+
+.cu-list.menu-avatar>.cu-item .content.flex-sub {
+	width: calc(100% - 96rpx - 60rpx - 20rpx);
+}
+
+.cu-list.menu-avatar>.cu-item .content>view:first-child {
+	font-size: 30rpx;
+	display: flex;
+	align-items: center
+}
+
+.cu-list.menu-avatar>.cu-item .content .cu-tag.sm {
+	display: inline-block;
+	margin-left: 10rpx;
+	height: 28rpx;
+	font-size: 16rpx;
+	line-height: 32rpx
+}
+
+.cu-list.menu-avatar>.cu-item .action {
+	width: 100rpx;
+	text-align: center
+}
+
+.cu-list.menu-avatar>.cu-item .action view+view {
+	margin-top: 10rpx
+}
+
+.cu-list.menu-avatar.comment>.cu-item .content {
+	position: relative;
+	left: 0;
+	width: auto;
+	flex: 1;
+}
+
+.cu-list.menu-avatar.comment>.cu-item {
+	padding: 30rpx 30rpx 30rpx 120rpx;
+	height: auto
+}
+
+.cu-list.menu-avatar.comment .cu-avatar {
+	align-self: flex-start
+}
+
+.cu-list.menu>.cu-item {
+	position: relative;
+	display: flex;
+	padding: 0 30rpx;
+	min-height: 100rpx;
+	background-color: var(--white);
+	justify-content: space-between;
+	align-items: center
+}
+
+.cu-list.menu>.cu-item:last-child:after {
+	border: none
+}
+
+.cu-list.menu>.cu-item:after {
+	position: absolute;
+	top: 0;
+	left: 0;
+	box-sizing: border-box;
+	width: 200%;
+	height: 200%;
+	border-bottom: 1rpx solid #ddd;
+	border-radius: inherit;
+	content: " ";
+	transform: scale(.5);
+	transform-origin: 0 0;
+	pointer-events: none
+}
+
+.cu-list.menu>.cu-item.grayscale {
+	background-color: #f5f5f5
+}
+
+.cu-list.menu>.cu-item.cur {
+	background-color: #fcf7e9
+}
+
+.cu-list.menu>.cu-item.arrow {
+	padding-right: 90rpx
+}
+
+.cu-list.menu>.cu-item.arrow:before {
+	position: absolute;
+	top: 0;
+	right: 30rpx;
+	bottom: 0;
+	display: block;
+	margin: auto;
+	width: 30rpx;
+	height: 30rpx;
+	color: var(--grey);
+	content: "\e6a3";
+	text-align: center;
+	font-size: 34rpx;
+	font-family: "cuIcon";
+	line-height: 30rpx
+}
+
+.cu-list.menu>.cu-item button.content {
+	padding: 0;
+	background-color: transparent;
+	justify-content: flex-start
+}
+
+.cu-list.menu>.cu-item button.content:after {
+	display: none
+}
+
+.cu-list.menu>.cu-item .cu-avatar-group .cu-avatar {
+	border-color: var(--white)
+}
+
+.cu-list.menu>.cu-item .content>view:first-child {
+	display: flex;
+	align-items: center
+}
+
+.cu-list.menu>.cu-item .content>text[class*=cuIcon] {
+	display: inline-block;
+	margin-right: 10rpx;
+	width: 1.6em;
+	text-align: center
+}
+
+.cu-list.menu>.cu-item .content>image {
+	display: inline-block;
+	margin-right: 10rpx;
+	width: 1.6em;
+	height: 1.6em;
+	vertical-align: middle
+}
+
+.cu-list.menu>.cu-item .content {
+	font-size: 30rpx;
+	line-height: 1.6em;
+	flex: 1
+}
+
+.cu-list.menu>.cu-item .content .cu-tag.sm {
+	display: inline-block;
+	margin-left: 10rpx;
+	height: 28rpx;
+	font-size: 16rpx;
+	line-height: 32rpx
+}
+
+.cu-list.menu>.cu-item .action .cu-tag:empty {
+	right: 10rpx
+}
+
+.cu-list.menu {
+	display: block;
+	overflow: hidden
+}
+
+.cu-list.menu.sm-border>.cu-item:after {
+	left: 30rpx;
+	width: calc(200% - 120rpx)
+}
+
+.cu-list.grid>.cu-item {
+	position: relative;
+	display: flex;
+	padding: 20rpx 0 30rpx;
+	transition-duration: 0s;
+	flex-direction: column
+}
+
+.cu-list.grid>.cu-item:after {
+	position: absolute;
+	top: 0;
+	left: 0;
+	box-sizing: border-box;
+	width: 200%;
+	height: 200%;
+	border-right: 1px solid rgba(0, 0, 0, .1);
+	border-bottom: 1px solid rgba(0, 0, 0, .1);
+	border-radius: inherit;
+	content: " ";
+	transform: scale(.5);
+	transform-origin: 0 0;
+	pointer-events: none
+}
+
+.cu-list.grid>.cu-item text {
+	display: block;
+	margin-top: 10rpx;
+	color: #888;
+	font-size: 26rpx;
+	line-height: 40rpx
+}
+
+.cu-list.grid>.cu-item [class*=cuIcon] {
+	position: relative;
+	display: block;
+	margin-top: 20rpx;
+	width: 100%;
+	font-size: 48rpx
+}
+
+.cu-list.grid>.cu-item .cu-tag {
+	right: auto;
+	left: 50%;
+	margin-left: 20rpx
+}
+
+.cu-list.grid {
+	background-color: var(--white);
+	text-align: center
+}
+
+.cu-list.grid.no-border>.cu-item {
+	padding-top: 10rpx;
+	padding-bottom: 20rpx
+}
+
+.cu-list.grid.no-border>.cu-item:after {
+	border: none
+}
+
+.cu-list.grid.no-border {
+	padding: 20rpx 10rpx
+}
+
+.cu-list.grid.col-3>.cu-item:nth-child(3n):after,
+.cu-list.grid.col-4>.cu-item:nth-child(4n):after,
+.cu-list.grid.col-5>.cu-item:nth-child(5n):after {
+	border-right-width: 0
+}
+
+.cu-list.card-menu {
+	overflow: hidden;
+	margin-right: 30rpx;
+	margin-left: 30rpx;
+	border-radius: 20rpx
+}
+
+
+/* ==================
+          操作条
+ ==================== */
+
+.cu-bar {
+	display: flex;
+	position: relative;
+	align-items: center;
+	min-height: 100rpx;
+	justify-content: space-between;
+}
+
+.cu-bar .action {
+	display: flex;
+	align-items: center;
+	height: 100%;
+	justify-content: center;
+	max-width: 100%;
+}
+
+.cu-bar .action.border-title {
+	position: relative;
+	top: -10rpx;
+}
+
+.cu-bar .action.border-title text[class*="bg-"]:last-child {
+	position: absolute;
+	bottom: -0.5rem;
+	min-width: 2rem;
+	height: 6rpx;
+	left: 0;
+}
+
+.cu-bar .action.sub-title {
+	position: relative;
+	top: -0.2rem;
+}
+
+.cu-bar .action.sub-title text {
+	position: relative;
+	z-index: 1;
+}
+
+.cu-bar .action.sub-title text[class*="bg-"]:last-child {
+	position: absolute;
+	display: inline-block;
+	bottom: -0.2rem;
+	border-radius: 6rpx;
+	width: 100%;
+	height: 0.6rem;
+	left: 0.6rem;
+	opacity: 0.3;
+	z-index: 0;
+}
+
+.cu-bar .action.sub-title text[class*="text-"]:last-child {
+	position: absolute;
+	display: inline-block;
+	bottom: -0.7rem;
+	left: 0.5rem;
+	opacity: 0.2;
+	z-index: 0;
+	text-align: right;
+	font-weight: 900;
+	font-size: 36rpx;
+}
+
+.cu-bar.justify-center .action.border-title text:last-child,
+.cu-bar.justify-center .action.sub-title text:last-child {
+	left: 0;
+	right: 0;
+	margin: auto;
+	text-align: center;
+}
+
+.cu-bar .action:first-child {
+	margin-left: 30rpx;
+	font-size: 30rpx;
+}
+
+.cu-bar .action text.text-cut {
+	text-align: left;
+	width: 100%;
+}
+
+.cu-bar .cu-avatar:first-child {
+	margin-left: 20rpx;
+}
+
+.cu-bar .action:first-child>text[class*="cuIcon-"] {
+	margin-left: -0.3em;
+	margin-right: 0.3em;
+}
+
+.cu-bar .action:last-child {
+	margin-right: 30rpx;
+}
+
+.cu-bar .action>text[class*="cuIcon-"],
+.cu-bar .action>view[class*="cuIcon-"] {
+	font-size: 36rpx;
+}
+
+.cu-bar .action>text[class*="cuIcon-"]+text[class*="cuIcon-"] {
+	margin-left: 0.5em;
+}
+
+.cu-bar .content {
+	position: absolute;
+	text-align: center;
+	width: calc(100% - 340rpx);
+	left: 0;
+	right: 0;
+	bottom: 0;
+	top: 0;
+	margin: auto;
+	height: 60rpx;
+	font-size: 32rpx;
+	line-height: 60rpx;
+	cursor: none;
+	pointer-events: none;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+	overflow: hidden;
+}
+
+.cu-bar.ios .content {
+	bottom: 7px;
+	height: 30px;
+	font-size: 32rpx;
+	line-height: 30px;
+}
+
+.cu-bar.btn-group {
+	justify-content: space-around;
+}
+
+.cu-bar.btn-group button {
+	padding: 20rpx 32rpx;
+}
+
+.cu-bar.btn-group button {
+	flex: 1;
+	margin: 0 20rpx;
+	max-width: 50%;
+}
+
+.cu-bar .search-form {
+	background-color: #f5f5f5;
+	line-height: 64rpx;
+	height: 64rpx;
+	font-size: 24rpx;
+	color: var(--black);
+	flex: 1;
+	display: flex;
+	align-items: center;
+	margin: 0 30rpx;
+}
+
+.cu-bar .search-form+.action {
+	margin-right: 30rpx;
+}
+
+.cu-bar .search-form input {
+	flex: 1;
+	padding-right: 30rpx;
+	height: 64rpx;
+	line-height: 64rpx;
+	font-size: 26rpx;
+	background-color: transparent;
+}
+
+.cu-bar .search-form [class*="cuIcon-"] {
+	margin: 0 0.5em 0 0.8em;
+}
+
+.cu-bar .search-form [class*="cuIcon-"]::before {
+	top: 0rpx;
+}
+
+.cu-bar.fixed,
+.nav.fixed {
+	position: fixed;
+	width: 100%;
+	top: 0;
+	z-index: 1024;
+	box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.1);
+}
+
+.cu-bar.foot {
+	position: fixed;
+	width: 100%;
+	bottom: 0;
+	z-index: 1024;
+	box-shadow: 0 -1rpx 6rpx rgba(0, 0, 0, 0.1);
+}
+
+.cu-bar.tabbar {
+	padding: 0;
+	height: calc(100rpx + env(safe-area-inset-bottom) / 2);
+	padding-bottom: calc(env(safe-area-inset-bottom) / 2);
+}
+
+.cu-tabbar-height {
+	min-height: 100rpx;
+	height: calc(100rpx + env(safe-area-inset-bottom) / 2);
+}
+
+.cu-bar.tabbar.shadow {
+	box-shadow: 0 -1rpx 6rpx rgba(0, 0, 0, 0.1);
+}
+
+.cu-bar.tabbar .action {
+	font-size: 22rpx;
+	position: relative;
+	flex: 1;
+	text-align: center;
+	padding: 0;
+	display: block;
+	height: auto;
+	line-height: 1;
+	margin: 0;
+	overflow: initial;
+}
+
+.cu-bar.tabbar.shop .action {
+	width: 140rpx;
+	flex: initial;
+}
+
+.cu-bar.tabbar .action.add-action {
+	position: relative;
+	z-index: 2;
+	padding-top: 50rpx;
+	background-color: inherit;
+}
+
+.cu-bar.tabbar .action.add-action [class*="cuIcon-"] {
+	position: absolute;
+	width: 70rpx;
+	z-index: 2;
+	height: 70rpx;
+	border-radius: 50%;
+	line-height: 70rpx;
+	font-size: 50rpx;
+	top: -35rpx;
+	left: 0;
+	right: 0;
+	margin: auto;
+	padding: 0;
+}
+
+.cu-bar.tabbar .action.add-action::after {
+	content: "";
+	position: absolute;
+	width: 100rpx;
+	height: 100rpx;
+	top: -50rpx;
+	left: 0;
+	right: 0;
+	margin: auto;
+	box-shadow: 0 -3rpx 8rpx rgba(0, 0, 0, 0.08);
+	border-radius: 50rpx;
+	background-color: inherit;
+	z-index: 0;
+}
+
+.cu-bar.tabbar .action.add-action::before {
+	content: "";
+	position: absolute;
+	width: 100rpx;
+	height: 30rpx;
+	bottom: 30rpx;
+	left: 0;
+	right: 0;
+	margin: auto;
+	background-color: inherit;
+	z-index: 1;
+}
+
+.cu-bar.tabbar .btn-group {
+	flex: 1;
+	display: flex;
+	justify-content: space-around;
+	align-items: center;
+	padding: 0 10rpx;
+}
+
+.cu-bar.tabbar button.action::after {
+	border: 0;
+}
+
+.cu-bar.tabbar .action [class*="cuIcon-"] {
+	width: 100rpx;
+	position: relative;
+	display: block;
+	height: auto;
+	margin: 0 auto 10rpx;
+	text-align: center;
+	font-size: 40rpx;
+}
+
+.cu-bar.tabbar .action .cuIcon-cu-image {
+	margin: 0 auto;
+}
+
+.cu-bar.tabbar .action .cuIcon-cu-image image {
+	width: 50rpx;
+	height: 50rpx;
+	display: inline-block;
+}
+
+.cu-bar.tabbar .submit {
+	align-items: center;
+	display: flex;
+	justify-content: center;
+	text-align: center;
+	position: relative;
+	flex: 2;
+	align-self: stretch;
+}
+
+.cu-bar.tabbar .submit:last-child {
+	flex: 2.6;
+}
+
+.cu-bar.tabbar .submit+.submit {
+	flex: 2;
+}
+
+.cu-bar.tabbar.border .action::before {
+	content: " ";
+	width: 200%;
+	height: 200%;
+	position: absolute;
+	top: 0;
+	left: 0;
+	transform: scale(0.5);
+	transform-origin: 0 0;
+	border-right: 1rpx solid rgba(0, 0, 0, 0.1);
+	z-index: 3;
+}
+
+.cu-bar.tabbar.border .action:last-child:before {
+	display: none;
+}
+
+.cu-bar.input {
+	padding-right: 20rpx;
+	background-color: var(--white);
+}
+
+.cu-bar.input input {
+	overflow: initial;
+	line-height: 64rpx;
+	height: 64rpx;
+	min-height: 64rpx;
+	flex: 1;
+	font-size: 30rpx;
+	margin: 0 20rpx;
+}
+
+.cu-bar.input .action {
+	margin-left: 20rpx;
+}
+
+.cu-bar.input .action [class*="cuIcon-"] {
+	font-size: 48rpx;
+}
+
+.cu-bar.input input+.action {
+	margin-right: 20rpx;
+	margin-left: 0rpx;
+}
+
+.cu-bar.input .action:first-child [class*="cuIcon-"] {
+	margin-left: 0rpx;
+}
+
+.cu-custom {
+	display: block;
+	position: relative;
+}
+
+.cu-custom .cu-bar .content {
+	width: calc(100% - 440rpx);
+}
+
+
+.cu-custom .cu-bar .content image {
+	height: 60rpx;
+	width: 240rpx;
+}
+
+.cu-custom .cu-bar {
+	min-height: 0px;
+	padding-right: 220rpx;
+	box-shadow: 0rpx 0rpx 0rpx;
+	z-index: 9999;
+}
+
+.cu-custom .cu-bar .border-custom {
+	position: relative;
+	background: rgba(0, 0, 0, 0.15);
+	border-radius: 1000rpx;
+	height: 30px;
+}
+
+.cu-custom .cu-bar .border-custom::after {
+	content: " ";
+	width: 200%;
+	height: 200%;
+	position: absolute;
+	top: 0;
+	left: 0;
+	border-radius: inherit;
+	transform: scale(0.5);
+	transform-origin: 0 0;
+	pointer-events: none;
+	box-sizing: border-box;
+	border: 1rpx solid var(--white);
+	opacity: 0.5;
+}
+
+.cu-custom .cu-bar .border-custom::before {
+	content: " ";
+	width: 1rpx;
+	height: 110%;
+	position: absolute;
+	top: 22.5%;
+	left: 0;
+	right: 0;
+	margin: auto;
+	transform: scale(0.5);
+	transform-origin: 0 0;
+	pointer-events: none;
+	box-sizing: border-box;
+	opacity: 0.6;
+	background-color: var(--white);
+}
+
+.cu-custom .cu-bar .border-custom text {
+	display: block;
+	flex: 1;
+	margin: auto !important;
+	text-align: center;
+	font-size: 34rpx;
+}
+
+/* ==================
+         导航栏
+ ==================== */
+
+.nav {
+	white-space: nowrap;
+}
+
+::-webkit-scrollbar {
+	display: none;
+}
+
+.nav .cu-item {
+	height: 90rpx;
+	display: inline-block;
+	line-height: 90rpx;
+	margin: 0 10rpx;
+	padding: 0 20rpx;
+}
+
+.nav .cu-item.cur {
+	border-bottom: 4rpx solid;
+}
+
+/* ==================
+         时间轴
+ ==================== */
+
+.cu-timeline {
+	display: block;
+	background-color: var(--white);
+}
+
+.cu-timeline .cu-time {
+	width: 120rpx;
+	text-align: center;
+	padding: 20rpx 0;
+	font-size: 26rpx;
+	color: #888;
+	display: block;
+}
+
+.cu-timeline>.cu-item {
+	padding: 30rpx 30rpx 30rpx 120rpx;
+	position: relative;
+	display: block;
+	z-index: 0;
+}
+
+.cu-timeline>.cu-item:not([class*="text-"]) {
+	color: #ccc;
+}
+
+.cu-timeline>.cu-item::after {
+	content: "";
+	display: block;
+	position: absolute;
+	width: 1rpx;
+	background-color: #ddd;
+	left: 60rpx;
+	height: 100%;
+	top: 0;
+	z-index: 8;
+}
+
+.cu-timeline>.cu-item::before {
+	font-family: "cuIcon";
+	display: block;
+	position: absolute;
+	top: 36rpx;
+	z-index: 9;
+	background-color: var(--white);
+	width: 50rpx;
+	height: 50rpx;
+	text-align: center;
+	border: none;
+	line-height: 50rpx;
+	left: 36rpx;
+}
+
+.cu-timeline>.cu-item:not([class*="cuIcon-"])::before {
+	content: "\e763";
+}
+
+.cu-timeline>.cu-item[class*="cuIcon-"]::before {
+	background-color: var(--white);
+	width: 50rpx;
+	height: 50rpx;
+	text-align: center;
+	border: none;
+	line-height: 50rpx;
+	left: 36rpx;
+}
+
+.cu-timeline>.cu-item>.content {
+	padding: 30rpx;
+	border-radius: 6rpx;
+	display: block;
+	line-height: 1.6;
+}
+
+.cu-timeline>.cu-item>.content:not([class*="bg-"]) {
+	background-color: var(--ghostWhite);
+	color: var(--black);
+}
+
+.cu-timeline>.cu-item>.content+.content {
+	margin-top: 20rpx;
+}
+
+/* ==================
+         聊天
+ ==================== */
+
+.cu-chat {
+	display: flex;
+	flex-direction: column;
+}
+
+.cu-chat .cu-item {
+	display: flex;
+	padding: 30rpx 30rpx 70rpx;
+	position: relative;
+}
+
+.cu-chat .cu-item>.cu-avatar {
+	width: 80rpx;
+	height: 80rpx;
+}
+
+.cu-chat .cu-item>.main {
+	max-width: calc(100% - 260rpx);
+	margin: 0 40rpx;
+	display: flex;
+	align-items: center;
+}
+
+.cu-chat .cu-item>image {
+	height: 320rpx;
+}
+
+.cu-chat .cu-item>.main .content {
+	padding: 20rpx;
+	border-radius: 6rpx;
+	display: inline-flex;
+	max-width: 100%;
+	align-items: center;
+	font-size: 30rpx;
+	position: relative;
+	min-height: 80rpx;
+	line-height: 40rpx;
+	text-align: left;
+}
+
+.cu-chat .cu-item>.main .content:not([class*="bg-"]) {
+	background-color: var(--white);
+	color: var(--black);
+}
+
+.cu-chat .cu-item .date {
+	position: absolute;
+	font-size: 24rpx;
+	color: var(--grey);
+	width: calc(100% - 320rpx);
+	bottom: 20rpx;
+	left: 160rpx;
+}
+
+.cu-chat .cu-item .action {
+	padding: 0 30rpx;
+	display: flex;
+	align-items: center;
+}
+
+.cu-chat .cu-item>.main .content::after {
+	content: "";
+	top: 27rpx;
+	transform: rotate(45deg);
+	position: absolute;
+	z-index: 100;
+	display: inline-block;
+	overflow: hidden;
+	width: 24rpx;
+	height: 24rpx;
+	left: -12rpx;
+	right: initial;
+	background-color: inherit;
+}
+
+.cu-chat .cu-item.self>.main .content::after {
+	left: auto;
+	right: -12rpx;
+}
+
+.cu-chat .cu-item>.main .content::before {
+	content: "";
+	top: 30rpx;
+	transform: rotate(45deg);
+	position: absolute;
+	z-index: -1;
+	display: inline-block;
+	overflow: hidden;
+	width: 24rpx;
+	height: 24rpx;
+	left: -12rpx;
+	right: initial;
+	background-color: inherit;
+	filter: blur(5rpx);
+	opacity: 0.3;
+}
+
+.cu-chat .cu-item>.main .content:not([class*="bg-"])::before {
+	background-color: var(--black);
+	opacity: 0.1;
+}
+
+.cu-chat .cu-item.self>.main .content::before {
+	left: auto;
+	right: -12rpx;
+}
+
+.cu-chat .cu-item.self {
+	justify-content: flex-end;
+	text-align: right;
+}
+
+.cu-chat .cu-info {
+	display: inline-block;
+	margin: 20rpx auto;
+	font-size: 24rpx;
+	padding: 8rpx 12rpx;
+	background-color: rgba(0, 0, 0, 0.2);
+	border-radius: 6rpx;
+	color: var(--white);
+	max-width: 400rpx;
+	line-height: 1.4;
+}
+
+/* ==================
+         卡片
+ ==================== */
+
+.cu-card {
+	display: block;
+	overflow: hidden;
+}
+
+.cu-card>.cu-item {
+	display: block;
+	background-color: var(--white);
+	overflow: hidden;
+	border-radius: 10rpx;
+	margin: 30rpx;
+}
+
+.cu-card>.cu-item.shadow-blur {
+	overflow: initial;
+}
+
+.cu-card.no-card>.cu-item {
+	margin: 0rpx;
+	border-radius: 0rpx;
+}
+
+.cu-card .grid.grid-square {
+	margin-bottom: -20rpx;
+}
+
+.cu-card.case .image {
+	position: relative;
+}
+
+.cu-card.case .image image {
+	width: 100%;
+}
+
+.cu-card.case .image .cu-tag {
+	position: absolute;
+	right: 0;
+	top: 0;
+}
+
+.cu-card.case .image .cu-bar {
+	position: absolute;
+	bottom: 0;
+	width: 100%;
+	background-color: transparent;
+	padding: 0rpx 30rpx;
+}
+
+.cu-card.case.no-card .image {
+	margin: 30rpx 30rpx 0;
+	overflow: hidden;
+	border-radius: 10rpx;
+}
+
+.cu-card.dynamic {
+	display: block;
+}
+
+.cu-card.dynamic>.cu-item {
+	display: block;
+	background-color: var(--white);
+	overflow: hidden;
+}
+
+.cu-card.dynamic>.cu-item>.text-content {
+	padding: 0 30rpx 0;
+	max-height: 6.4em;
+	overflow: hidden;
+	font-size: 30rpx;
+	margin-bottom: 20rpx;
+}
+
+.cu-card.dynamic>.cu-item .square-img {
+	width: 100%;
+	height: 200rpx;
+	border-radius: 6rpx;
+}
+
+.cu-card.dynamic>.cu-item .only-img {
+	width: 100%;
+	height: 320rpx;
+	border-radius: 6rpx;
+}
+
+.cu-card.article {
+	display: block;
+}
+
+.cu-card.article>.cu-item {
+	padding-bottom: 30rpx;
+}
+
+.cu-card.article>.cu-item .title {
+	font-size: 30rpx;
+	font-weight: 900;
+	color: var(--black);
+	line-height: 100rpx;
+	padding: 0 30rpx;
+}
+
+.cu-card.article>.cu-item .content {
+	display: flex;
+	padding: 0 30rpx;
+}
+
+.cu-card.article>.cu-item .content>image {
+	width: 240rpx;
+	height: 6.4em;
+	margin-right: 20rpx;
+	border-radius: 6rpx;
+}
+
+.cu-card.article>.cu-item .content .desc {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+	justify-content: space-between;
+}
+
+.cu-card.article>.cu-item .content .text-content {
+	font-size: 28rpx;
+	color: #888;
+	height: 4.8em;
+	overflow: hidden;
+}
+
+/* ==================
+         表单
+ ==================== */
+
+.cu-form-group {
+	background-color: var(--white);
+	padding: 1rpx 30rpx;
+	display: flex;
+	align-items: center;
+	min-height: 100rpx;
+	justify-content: space-between;
+}
+
+.cu-form-group+.cu-form-group {
+	border-top: 1rpx solid #eee;
+}
+
+.cu-form-group .title {
+	text-align: justify;
+	padding-right: 30rpx;
+	font-size: 30rpx;
+	position: relative;
+	height: 60rpx;
+	line-height: 60rpx;
+}
+
+.cu-form-group input {
+	flex: 1;
+	font-size: 30rpx;
+	color: #555;
+	padding-right: 20rpx;
+}
+
+.cu-form-group>text[class*="cuIcon-"] {
+	font-size: 36rpx;
+	padding: 0;
+	box-sizing: border-box;
+}
+
+.cu-form-group textarea {
+	margin: 32rpx 0 30rpx;
+	height: 4.6em;
+	width: 100%;
+	line-height: 1.2em;
+	flex: 1;
+	font-size: 28rpx;
+	padding: 0;
+}
+
+.cu-form-group.align-start .title {
+	height: 1em;
+	margin-top: 32rpx;
+	line-height: 1em;
+}
+
+.cu-form-group picker {
+	flex: 1;
+	padding-right: 40rpx;
+	overflow: hidden;
+	position: relative;
+}
+
+.cu-form-group picker .picker {
+	line-height: 100rpx;
+	font-size: 28rpx;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+	overflow: hidden;
+	width: 100%;
+	text-align: right;
+}
+
+.cu-form-group picker::after {
+	font-family: "cuIcon";
+	display: block;
+	content: "\e6a3";
+	position: absolute;
+	font-size: 34rpx;
+	color: var(--grey);
+	line-height: 100rpx;
+	width: 60rpx;
+	text-align: center;
+	top: 0;
+	bottom: 0;
+	right: -20rpx;
+	margin: auto;
+}
+
+.cu-form-group textarea[disabled],
+.cu-form-group textarea[disabled] .placeholder {
+	color: transparent;
+}
+
+/* ==================
+         模态窗口
+ ==================== */
+
+.cu-modal {
+	position: fixed;
+	top: 0;
+	right: 0;
+	bottom: 0;
+	left: 0;
+	z-index: 1110;
+	opacity: 0;
+	outline: 0;
+	text-align: center;
+	-ms-transform: scale(1.185);
+	transform: scale(1.185);
+	backface-visibility: hidden;
+	perspective: 2000rpx;
+	background: rgba(0, 0, 0, 0.6);
+	transition: all 0.3s ease-in-out 0s;
+	pointer-events: none;
+}
+
+.cu-modal::before {
+	content: "\200B";
+	display: inline-block;
+	height: 100%;
+	vertical-align: middle;
+}
+
+.cu-modal.show {
+	opacity: 1;
+	transition-duration: 0.3s;
+	-ms-transform: scale(1);
+	transform: scale(1);
+	overflow-x: hidden;
+	overflow-y: auto;
+	pointer-events: auto;
+}
+
+.cu-dialog {
+	position: relative;
+	display: inline-block;
+	vertical-align: middle;
+	margin-left: auto;
+	margin-right: auto;
+	width: 680rpx;
+	max-width: 100%;
+	background-color: #f8f8f8;
+	border-radius: 10rpx;
+	overflow: hidden;
+}
+
+.cu-modal.bottom-modal::before {
+	vertical-align: bottom;
+}
+
+.cu-modal.bottom-modal .cu-dialog {
+	width: 100%;
+	border-radius: 0;
+}
+
+.cu-modal.bottom-modal {
+	margin-bottom: -1000rpx;
+}
+
+.cu-modal.bottom-modal.show {
+	margin-bottom: 0;
+}
+
+.cu-modal.drawer-modal {
+	transform: scale(1);
+	display: flex;
+}
+
+.cu-modal.drawer-modal .cu-dialog {
+	height: 100%;
+	min-width: 200rpx;
+	border-radius: 0;
+	margin: initial;
+	transition-duration: 0.3s;
+}
+
+.cu-modal.drawer-modal.justify-start .cu-dialog {
+	transform: translateX(-100%);
+}
+
+.cu-modal.drawer-modal.justify-end .cu-dialog {
+	transform: translateX(100%);
+}
+
+.cu-modal.drawer-modal.show .cu-dialog {
+	transform: translateX(0%);
+}
+.cu-modal .cu-dialog>.cu-bar:first-child .action{
+  min-width: 100rpx;
+  margin-right: 0;
+  min-height: 100rpx;
+}
+/* ==================
+         轮播
+ ==================== */
+swiper .a-swiper-dot {
+	display: inline-block;
+	width: 16rpx;
+	height: 16rpx;
+	background: rgba(0, 0, 0, .3);
+	border-radius: 50%;
+	vertical-align: middle;
+}
+
+swiper[class*="-dot"] .wx-swiper-dots {
+	display: flex;
+	align-items: center;
+	width: 100%;
+	justify-content: center;
+}
+
+swiper.square-dot .wx-swiper-dot {
+	background-color: var(--white);
+	opacity: 0.4;
+	width: 10rpx;
+	height: 10rpx;
+	border-radius: 20rpx;
+	margin: 0 8rpx !important;
+}
+
+swiper.square-dot .wx-swiper-dot.wx-swiper-dot-active {
+	opacity: 1;
+	width: 30rpx;
+}
+
+swiper.round-dot .wx-swiper-dot {
+	width: 10rpx;
+	height: 10rpx;
+	position: relative;
+	margin: 4rpx 8rpx !important;
+}
+
+swiper.round-dot .wx-swiper-dot.wx-swiper-dot-active::after {
+	content: "";
+	position: absolute;
+	width: 10rpx;
+	height: 10rpx;
+	top: 0rpx;
+	left: 0rpx;
+	right: 0;
+	bottom: 0;
+	margin: auto;
+	background-color: var(--white);
+	border-radius: 20rpx;
+}
+
+swiper.round-dot .wx-swiper-dot.wx-swiper-dot-active {
+	width: 18rpx;
+	height: 18rpx;
+}
+
+.screen-swiper {
+	min-height: 375rpx;
+}
+
+.screen-swiper image,
+.screen-swiper video,
+.swiper-item image,
+.swiper-item video {
+	width: 100%;
+	display: block;
+	height: 100%;
+	margin: 0;
+	pointer-events: none;
+}
+
+.card-swiper {
+	height: 420rpx !important;
+}
+
+.card-swiper swiper-item {
+	width: 610rpx !important;
+	left: 70rpx;
+	box-sizing: border-box;
+	padding: 40rpx 0rpx 70rpx;
+	overflow: initial;
+}
+
+.card-swiper swiper-item .swiper-item {
+	width: 100%;
+	display: block;
+	height: 100%;
+	border-radius: 10rpx;
+	transform: scale(0.9);
+	transition: all 0.2s ease-in 0s;
+	overflow: hidden;
+}
+
+.card-swiper swiper-item.cur .swiper-item {
+	transform: none;
+	transition: all 0.2s ease-in 0s;
+}
+
+
+.tower-swiper {
+	height: 420rpx;
+	position: relative;
+	max-width: 750rpx;
+	overflow: hidden;
+}
+
+.tower-swiper .tower-item {
+	position: absolute;
+	width: 300rpx;
+	height: 380rpx;
+	top: 0;
+	bottom: 0;
+	left: 50%;
+	margin: auto;
+	transition: all 0.2s ease-in 0s;
+	opacity: 1;
+}
+
+.tower-swiper .tower-item.none {
+	opacity: 0;
+}
+
+.tower-swiper .tower-item .swiper-item {
+	width: 100%;
+	height: 100%;
+	border-radius: 6rpx;
+	overflow: hidden;
+}
+
+/* ==================
+          步骤条
+ ==================== */
+
+.cu-steps {
+	display: flex;
+}
+
+scroll-view.cu-steps {
+	display: block;
+	white-space: nowrap;
+}
+
+scroll-view.cu-steps .cu-item {
+	display: inline-block;
+}
+
+.cu-steps .cu-item {
+	flex: 1;
+	text-align: center;
+	position: relative;
+	min-width: 100rpx;
+}
+
+.cu-steps .cu-item:not([class*="text-"]) {
+	color: var(--grey);
+}
+
+.cu-steps .cu-item [class*="cuIcon-"],
+.cu-steps .cu-item .num {
+	display: block;
+	font-size: 40rpx;
+	line-height: 80rpx;
+}
+
+.cu-steps .cu-item::before,
+.cu-steps .cu-item::after,
+.cu-steps.steps-arrow .cu-item::before,
+.cu-steps.steps-arrow .cu-item::after {
+	content: "";
+	display: block;
+	position: absolute;
+	height: 0px;
+	width: calc(100% - 80rpx);
+	border-bottom: 1px solid #ccc;
+	left: calc(0px - (100% - 80rpx) / 2);
+	top: 40rpx;
+	z-index: 0;
+}
+
+.cu-steps.steps-arrow .cu-item::before,
+.cu-steps.steps-arrow .cu-item::after {
+	content: "\e6a3";
+	font-family: "cuIcon";
+	height: 30rpx;
+	border-bottom-width: 0px;
+	line-height: 30rpx;
+	top: 0;
+	bottom: 0;
+	margin: auto;
+	color: #ccc;
+}
+
+.cu-steps.steps-bottom .cu-item::before,
+.cu-steps.steps-bottom .cu-item::after {
+	bottom: 40rpx;
+	top: initial;
+}
+
+.cu-steps .cu-item::after {
+	border-bottom: 1px solid currentColor;
+	width: 0px;
+	transition: all 0.3s ease-in-out 0s;
+}
+
+.cu-steps .cu-item[class*="text-"]::after {
+	width: calc(100% - 80rpx);
+	color: currentColor;
+}
+
+.cu-steps .cu-item:first-child::before,
+.cu-steps .cu-item:first-child::after {
+	display: none;
+}
+
+.cu-steps .cu-item .num {
+	width: 40rpx;
+	height: 40rpx;
+	border-radius: 50%;
+	line-height: 40rpx;
+	margin: 20rpx auto;
+	font-size: 24rpx;
+	border: 1px solid currentColor;
+	position: relative;
+	overflow: hidden;
+}
+
+.cu-steps .cu-item[class*="text-"] .num {
+	background-color: currentColor;
+}
+
+.cu-steps .cu-item .num::before,
+.cu-steps .cu-item .num::after {
+	content: attr(data-index);
+	position: absolute;
+	left: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+	margin: auto;
+	transition: all 0.3s ease-in-out 0s;
+	transform: translateY(0rpx);
+}
+
+.cu-steps .cu-item[class*="text-"] .num::before {
+	transform: translateY(-40rpx);
+	color: var(--white);
+}
+
+.cu-steps .cu-item .num::after {
+	transform: translateY(40rpx);
+	color: var(--white);
+	transition: all 0.3s ease-in-out 0s;
+}
+
+.cu-steps .cu-item[class*="text-"] .num::after {
+	content: "\e645";
+	font-family: "cuIcon";
+	color: var(--white);
+	transform: translateY(0rpx);
+}
+
+.cu-steps .cu-item[class*="text-"] .num.err::after {
+	content: "\e646";
+}
+
+/* ==================
+          布局
+ ==================== */
+
+/*  -- flex弹性布局 -- */
+
+.flex {
+	display: flex;
+}
+
+.basis-xs {
+	flex-basis: 20%;
+}
+
+.basis-sm {
+	flex-basis: 40%;
+}
+
+.basis-df {
+	flex-basis: 50%;
+}
+
+.basis-lg {
+	flex-basis: 60%;
+}
+
+.basis-xl {
+	flex-basis: 80%;
+}
+
+.flex-sub {
+	flex: 1;
+}
+
+.flex-twice {
+	flex: 2;
+}
+
+.flex-treble {
+	flex: 3;
+}
+
+.flex-direction {
+	flex-direction: column;
+}
+
+.flex-wrap {
+	flex-wrap: wrap;
+}
+
+.align-start {
+	align-items: flex-start;
+}
+
+.align-end {
+	align-items: flex-end;
+}
+
+.align-center {
+	align-items: center;
+}
+
+.align-stretch {
+	align-items: stretch;
+}
+
+.self-start {
+	align-self: flex-start;
+}
+
+.self-center {
+	align-self: flex-center;
+}
+
+.self-end {
+	align-self: flex-end;
+}
+
+.self-stretch {
+	align-self: stretch;
+}
+
+.align-stretch {
+	align-items: stretch;
+}
+
+.justify-start {
+	justify-content: flex-start;
+}
+
+.justify-end {
+	justify-content: flex-end;
+}
+
+.justify-center {
+	justify-content: center;
+}
+
+.justify-between {
+	justify-content: space-between;
+}
+
+.justify-around {
+	justify-content: space-around;
+}
+
+/* grid布局 */
+
+.grid {
+	display: flex;
+	flex-wrap: wrap;
+}
+
+.grid.grid-square {
+	overflow: hidden;
+}
+
+.grid.grid-square .cu-tag {
+	position: absolute;
+	right: 0;
+	top: 0;
+	border-bottom-left-radius: 6rpx;
+	padding: 6rpx 12rpx;
+	height: auto;
+	background-color: rgba(0, 0, 0, 0.5);
+}
+
+.grid.grid-square>view>text[class*="cuIcon-"] {
+	font-size: 52rpx;
+	position: absolute;
+	color: var(--grey);
+	margin: auto;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	flex-direction: column;
+}
+
+.grid.grid-square>view {
+	margin-right: 20rpx;
+	margin-bottom: 20rpx;
+	border-radius: 6rpx;
+	position: relative;
+	overflow: hidden;
+}
+
+.grid.grid-square>view.bg-img image {
+	width: 100%;
+	height: 100%;
+	position: absolute;
+}
+
+.grid.col-1.grid-square>view {
+	padding-bottom: 100%;
+	height: 0;
+	margin-right: 0;
+}
+
+.grid.col-2.grid-square>view {
+	padding-bottom: calc((100% - 20rpx)/2);
+	height: 0;
+	width: calc((100% - 20rpx)/2);
+}
+
+.grid.col-3.grid-square>view {
+	padding-bottom: calc((100% - 40rpx)/3);
+	height: 0;
+	width: calc((100% - 40rpx)/3);
+}
+
+.grid.col-4.grid-square>view {
+	padding-bottom: calc((100% - 60rpx)/4);
+	height: 0;
+	width: calc((100% - 60rpx)/4);
+}
+
+.grid.col-5.grid-square>view {
+	padding-bottom: calc((100% - 80rpx)/5);
+	height: 0;
+	width: calc((100% - 80rpx)/5);
+}
+
+.grid.col-2.grid-square>view:nth-child(2n),
+.grid.col-3.grid-square>view:nth-child(3n),
+.grid.col-4.grid-square>view:nth-child(4n),
+.grid.col-5.grid-square>view:nth-child(5n){
+	margin-right: 0;
+}
+
+.grid.col-1>view {
+	width: 100%;
+}
+
+.grid.col-2>view {
+	width: 50%;
+}
+
+.grid.col-3>view {
+	width: 33.33%;
+}
+
+.grid.col-4>view {
+	width: 25%;
+}
+
+.grid.col-5>view {
+	width: 20%;
+}
+
+/*  -- 内外边距 -- */
+
+.margin-0 {
+	margin: 0;
+}
+
+.margin-xs {
+	margin: 10rpx;
+}
+
+.margin-sm {
+	margin: 20rpx;
+}
+
+.margin {
+	margin: 30rpx;
+}
+
+.margin-lg {
+	margin: 40rpx;
+}
+
+.margin-xl {
+	margin: 50rpx;
+}
+
+.margin-top-xs {
+	margin-top: 10rpx;
+}
+
+.margin-top-sm {
+	margin-top: 20rpx;
+}
+
+.margin-top {
+	margin-top: 30rpx;
+}
+
+.margin-top-lg {
+	margin-top: 40rpx;
+}
+
+.margin-top-xl {
+	margin-top: 50rpx;
+}
+
+.margin-right-xs {
+	margin-right: 10rpx;
+}
+
+.margin-right-sm {
+	margin-right: 20rpx;
+}
+
+.margin-right {
+	margin-right: 30rpx;
+}
+
+.margin-right-lg {
+	margin-right: 40rpx;
+}
+
+.margin-right-xl {
+	margin-right: 50rpx;
+}
+
+.margin-bottom-xs {
+	margin-bottom: 10rpx;
+}
+
+.margin-bottom-sm {
+	margin-bottom: 20rpx;
+}
+
+.margin-bottom {
+	margin-bottom: 30rpx;
+}
+
+.margin-bottom-lg {
+	margin-bottom: 40rpx;
+}
+
+.margin-bottom-xl {
+	margin-bottom: 50rpx;
+}
+
+.margin-left-xs {
+	margin-left: 10rpx;
+}
+
+.margin-left-sm {
+	margin-left: 20rpx;
+}
+
+.margin-left {
+	margin-left: 30rpx;
+}
+
+.margin-left-lg {
+	margin-left: 40rpx;
+}
+
+.margin-left-xl {
+	margin-left: 50rpx;
+}
+
+.margin-lr-xs {
+	margin-left: 10rpx;
+	margin-right: 10rpx;
+}
+
+.margin-lr-sm {
+	margin-left: 20rpx;
+	margin-right: 20rpx;
+}
+
+.margin-lr {
+	margin-left: 30rpx;
+	margin-right: 30rpx;
+}
+
+.margin-lr-lg {
+	margin-left: 40rpx;
+	margin-right: 40rpx;
+}
+
+.margin-lr-xl {
+	margin-left: 50rpx;
+	margin-right: 50rpx;
+}
+
+.margin-tb-xs {
+	margin-top: 10rpx;
+	margin-bottom: 10rpx;
+}
+
+.margin-tb-sm {
+	margin-top: 20rpx;
+	margin-bottom: 20rpx;
+}
+
+.margin-tb {
+	margin-top: 30rpx;
+	margin-bottom: 30rpx;
+}
+
+.margin-tb-lg {
+	margin-top: 40rpx;
+	margin-bottom: 40rpx;
+}
+
+.margin-tb-xl {
+	margin-top: 50rpx;
+	margin-bottom: 50rpx;
+}
+
+.padding-0 {
+	padding: 0;
+}
+
+.padding-xs {
+	padding: 10rpx;
+}
+
+.padding-sm {
+	padding: 20rpx;
+}
+
+.padding {
+	padding: 30rpx;
+}
+
+.padding-lg {
+	padding: 40rpx;
+}
+
+.padding-xl {
+	padding: 50rpx;
+}
+
+.padding-top-xs {
+	padding-top: 10rpx;
+}
+
+.padding-top-sm {
+	padding-top: 20rpx;
+}
+
+.padding-top {
+	padding-top: 30rpx;
+}
+
+.padding-top-lg {
+	padding-top: 40rpx;
+}
+
+.padding-top-xl {
+	padding-top: 50rpx;
+}
+
+.padding-right-xs {
+	padding-right: 10rpx;
+}
+
+.padding-right-sm {
+	padding-right: 20rpx;
+}
+
+.padding-right {
+	padding-right: 30rpx;
+}
+
+.padding-right-lg {
+	padding-right: 40rpx;
+}
+
+.padding-right-xl {
+	padding-right: 50rpx;
+}
+
+.padding-bottom-xs {
+	padding-bottom: 10rpx;
+}
+
+.padding-bottom-sm {
+	padding-bottom: 20rpx;
+}
+
+.padding-bottom {
+	padding-bottom: 30rpx;
+}
+
+.padding-bottom-lg {
+	padding-bottom: 40rpx;
+}
+
+.padding-bottom-xl {
+	padding-bottom: 50rpx;
+}
+
+.padding-left-xs {
+	padding-left: 10rpx;
+}
+
+.padding-left-sm {
+	padding-left: 20rpx;
+}
+
+.padding-left {
+	padding-left: 30rpx;
+}
+
+.padding-left-lg {
+	padding-left: 40rpx;
+}
+
+.padding-left-xl {
+	padding-left: 50rpx;
+}
+
+.padding-lr-xs {
+	padding-left: 10rpx;
+	padding-right: 10rpx;
+}
+
+.padding-lr-sm {
+	padding-left: 20rpx;
+	padding-right: 20rpx;
+}
+
+.padding-lr {
+	padding-left: 30rpx;
+	padding-right: 30rpx;
+}
+
+.padding-lr-lg {
+	padding-left: 40rpx;
+	padding-right: 40rpx;
+}
+
+.padding-lr-xl {
+	padding-left: 50rpx;
+	padding-right: 50rpx;
+}
+
+.padding-tb-xs {
+	padding-top: 10rpx;
+	padding-bottom: 10rpx;
+}
+
+.padding-tb-sm {
+	padding-top: 20rpx;
+	padding-bottom: 20rpx;
+}
+
+.padding-tb {
+	padding-top: 30rpx;
+	padding-bottom: 30rpx;
+}
+
+.padding-tb-lg {
+	padding-top: 40rpx;
+	padding-bottom: 40rpx;
+}
+
+.padding-tb-xl {
+	padding-top: 50rpx;
+	padding-bottom: 50rpx;
+}
+
+/* -- 浮动 --  */
+
+.cf::after,
+.cf::before {
+	content: " ";
+	display: table;
+}
+
+.cf::after {
+	clear: both;
+}
+
+.fl {
+	float: left;
+}
+
+.fr {
+	float: right;
+}
+
+/* ==================
+          背景
+ ==================== */
+
+.line-red::after,
+.lines-red::after {
+	border-color: var(--red);
+}
+
+.line-orange::after,
+.lines-orange::after {
+	border-color: var(--orange);
+}
+
+.line-yellow::after,
+.lines-yellow::after {
+	border-color: var(--yellow);
+}
+
+.line-olive::after,
+.lines-olive::after {
+	border-color: var(--olive);
+}
+
+.line-green::after,
+.lines-green::after {
+	border-color: var(--green);
+}
+
+.line-cyan::after,
+.lines-cyan::after {
+	border-color: var(--cyan);
+}
+
+.line-blue::after,
+.lines-blue::after {
+	border-color: var(--blue);
+}
+
+.line-purple::after,
+.lines-purple::after {
+	border-color: var(--purple);
+}
+
+.line-mauve::after,
+.lines-mauve::after {
+	border-color: var(--mauve);
+}
+
+.line-pink::after,
+.lines-pink::after {
+	border-color: var(--pink);
+}
+
+.line-brown::after,
+.lines-brown::after {
+	border-color: var(--brown);
+}
+
+.line-grey::after,
+.lines-grey::after {
+	border-color: var(--grey);
+}
+
+.line-gray::after,
+.lines-gray::after {
+	border-color: var(--gray);
+}
+
+.line-black::after,
+.lines-black::after {
+	border-color: var(--black);
+}
+
+.line-white::after,
+.lines-white::after {
+	border-color: var(--white);
+}
+
+.bg-red {
+	background-color: var(--red);
+	color: var(--white);
+}
+
+.bg-orange {
+	background-color: var(--orange);
+	color: var(--white);
+}
+
+.bg-yellow {
+	background-color: var(--yellow);
+	color: var(--black);
+}
+
+.bg-olive {
+	background-color: var(--olive);
+	color: var(--white);
+}
+
+.bg-green {
+	background-color: var(--green);
+	color: var(--white);
+}
+
+.bg-cyan {
+	background-color: var(--cyan);
+	color: var(--white);
+}
+
+.bg-blue {
+	background-color: var(--blue);
+	color: var(--white);
+}
+
+.bg-purple {
+	background-color: var(--purple);
+	color: var(--white);
+}
+
+.bg-mauve {
+	background-color: var(--mauve);
+	color: var(--white);
+}
+
+.bg-pink {
+	background-color: var(--pink);
+	color: var(--white);
+}
+
+.bg-brown {
+	background-color: var(--brown);
+	color: var(--white);
+}
+
+.bg-grey {
+	background-color: var(--grey);
+	color: var(--white);
+}
+
+.bg-gray {
+	background-color: #f0f0f0;
+	color: var(--black);
+}
+
+.bg-black {
+	background-color: var(--black);
+	color: var(--white);
+}
+
+.bg-white {
+	background-color: var(--white);
+	color: var(--darkGray);
+}
+
+.bg-shadeTop {
+	background-image: linear-gradient(rgba(0, 0, 0, 1), rgba(0, 0, 0, 0.01));
+	color: var(--white);
+}
+
+.bg-shadeBottom {
+	background-image: linear-gradient(rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 1));
+	color: var(--white);
+}
+
+.bg-red.light {
+	color: var(--red);
+	background-color: var(--redLight);
+}
+
+.bg-orange.light {
+	color: var(--orange);
+	background-color: var(--orangeLight);
+}
+
+.bg-yellow.light {
+	color: var(--yellow);
+	background-color: var(--yellowLight);
+}
+
+.bg-olive.light {
+	color: var(--olive);
+	background-color: var(--oliveLight);
+}
+
+.bg-green.light {
+	color: var(--green);
+	background-color: var(--greenLight);
+}
+
+.bg-cyan.light {
+	color: var(--cyan);
+	background-color: var(--cyanLight);
+}
+
+.bg-blue.light {
+	color: var(--blue);
+	background-color: var(--blueLight);
+}
+
+.bg-purple.light {
+	color: var(--purple);
+	background-color: var(--purpleLight);
+}
+
+.bg-mauve.light {
+	color: var(--mauve);
+	background-color: var(--mauveLight);
+}
+
+.bg-pink.light {
+	color: var(--pink);
+	background-color: var(--pinkLight);
+}
+
+.bg-brown.light {
+	color: var(--brown);
+	background-color: var(--brownLight);
+}
+
+.bg-grey.light {
+	color: var(--grey);
+	background-color: var(--greyLight);
+}
+
+.bg-gradual-red {
+	background-image: var(--gradualRed);
+	color: var(--white);
+}
+
+.bg-gradual-orange {
+	background-image: var(--gradualOrange);
+	color: var(--white);
+}
+
+.bg-gradual-green {
+	background-image: var(--gradualGreen);
+	color: var(--white);
+}
+
+.bg-gradual-purple {
+	background-image: var(--gradualPurple);
+	color: var(--white);
+}
+
+.bg-gradual-pink {
+	background-image: var(--gradualPink);
+	color: var(--white);
+}
+
+.bg-gradual-blue {
+	background-image: var(--gradualBlue);
+	color: var(--white);
+}
+
+.shadow[class*="-red"] {
+	box-shadow: var(--ShadowSize) var(--redShadow);
+}
+
+.shadow[class*="-orange"] {
+	box-shadow: var(--ShadowSize) var(--orangeShadow);
+}
+
+.shadow[class*="-yellow"] {
+	box-shadow: var(--ShadowSize) var(--yellowShadow);
+}
+
+.shadow[class*="-olive"] {
+	box-shadow: var(--ShadowSize) var(--oliveShadow);
+}
+
+.shadow[class*="-green"] {
+	box-shadow: var(--ShadowSize) var(--greenShadow);
+}
+
+.shadow[class*="-cyan"] {
+	box-shadow: var(--ShadowSize) var(--cyanShadow);
+}
+
+.shadow[class*="-blue"] {
+	box-shadow: var(--ShadowSize) var(--blueShadow);
+}
+
+.shadow[class*="-purple"] {
+	box-shadow: var(--ShadowSize) var(--purpleShadow);
+}
+
+.shadow[class*="-mauve"] {
+	box-shadow: var(--ShadowSize) var(--mauveShadow);
+}
+
+.shadow[class*="-pink"] {
+	box-shadow: var(--ShadowSize) var(--pinkShadow);
+}
+
+.shadow[class*="-brown"] {
+	box-shadow: var(--ShadowSize) var(--brownShadow);
+}
+
+.shadow[class*="-grey"] {
+	box-shadow: var(--ShadowSize) var(--greyShadow);
+}
+
+.shadow[class*="-gray"] {
+	box-shadow: var(--ShadowSize) var(--grayShadow);
+}
+
+.shadow[class*="-black"] {
+	box-shadow: var(--ShadowSize) var(--blackShadow);
+}
+
+.shadow[class*="-white"] {
+	box-shadow: var(--ShadowSize) var(--blackShadow);
+}
+
+.text-shadow[class*="-red"] {
+	text-shadow: var(--ShadowSize) var(--redShadow);
+}
+
+.text-shadow[class*="-orange"] {
+	text-shadow: var(--ShadowSize) var(--orangeShadow);
+}
+
+.text-shadow[class*="-yellow"] {
+	text-shadow: var(--ShadowSize) var(--yellowShadow);
+}
+
+.text-shadow[class*="-olive"] {
+	text-shadow: var(--ShadowSize) var(--oliveShadow);
+}
+
+.text-shadow[class*="-green"] {
+	text-shadow: var(--ShadowSize) var(--greenShadow);
+}
+
+.text-shadow[class*="-cyan"] {
+	text-shadow: var(--ShadowSize) var(--cyanShadow);
+}
+
+.text-shadow[class*="-blue"] {
+	text-shadow: var(--ShadowSize) var(--blueShadow);
+}
+
+.text-shadow[class*="-purple"] {
+	text-shadow: var(--ShadowSize) var(--purpleShadow);
+}
+
+.text-shadow[class*="-mauve"] {
+	text-shadow: var(--ShadowSize) var(--mauveShadow);
+}
+
+.text-shadow[class*="-pink"] {
+	text-shadow: var(--ShadowSize) var(--pinkShadow);
+}
+
+.text-shadow[class*="-brown"] {
+	text-shadow: var(--ShadowSize) var(--brownShadow);
+}
+
+.text-shadow[class*="-grey"] {
+	text-shadow: var(--ShadowSize) var(--greyShadow);
+}
+
+.text-shadow[class*="-gray"] {
+	text-shadow: var(--ShadowSize) var(--grayShadow);
+}
+
+.text-shadow[class*="-black"] {
+	text-shadow: var(--ShadowSize) var(--blackShadow);
+}
+
+.bg-img {
+	background-size: cover;
+	background-position: center;
+	background-repeat: no-repeat;
+}
+
+.bg-mask {
+	background-color: var(--black);
+	position: relative;
+}
+
+.bg-mask::after {
+	content: "";
+	border-radius: inherit;
+	width: 100%;
+	height: 100%;
+	display: block;
+	background-color: rgba(0, 0, 0, 0.4);
+	position: absolute;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	top: 0;
+}
+
+.bg-mask view,
+.bg-mask cover-view {
+	z-index: 5;
+	position: relative;
+}
+
+.bg-video {
+	position: relative;
+}
+
+.bg-video video {
+	display: block;
+	height: 100%;
+	width: 100%;
+	-o-object-fit: cover;
+	object-fit: cover;
+	position: absolute;
+	top: 0;
+	z-index: 0;
+	pointer-events: none;
+}
+
+/* ==================
+          文本
+ ==================== */
+
+.text-xs {
+	font-size: 20rpx;
+}
+
+.text-sm {
+	font-size: 24rpx;
+}
+
+.text-df {
+	font-size: 28rpx;
+}
+
+.text-lg {
+	font-size: 32rpx;
+}
+
+.text-xl {
+	font-size: 36rpx;
+}
+
+.text-xxl {
+	font-size: 44rpx;
+}
+
+.text-sl {
+	font-size: 80rpx;
+}
+
+.text-xsl {
+	font-size: 120rpx;
+}
+
+.text-Abc {
+	text-transform: Capitalize;
+}
+
+.text-ABC {
+	text-transform: Uppercase;
+}
+
+.text-abc {
+	text-transform: Lowercase;
+}
+
+.text-price::before {
+	content: "¥";
+	font-size: 80%;
+	margin-right: 4rpx;
+}
+
+.text-cut {
+	text-overflow: ellipsis;
+	white-space: nowrap;
+	overflow: hidden;
+}
+
+.text-bold {
+	font-weight: bold;
+}
+
+.text-center {
+	text-align: center;
+}
+
+.text-content {
+	line-height: 1.6;
+}
+
+.text-left {
+	text-align: left;
+}
+
+.text-right {
+	text-align: right;
+}
+
+.text-red,
+.line-red,
+.lines-red {
+	color: var(--red);
+}
+
+.text-orange,
+.line-orange,
+.lines-orange {
+	color: var(--orange);
+}
+
+.text-yellow,
+.line-yellow,
+.lines-yellow {
+	color: var(--yellow);
+}
+
+.text-olive,
+.line-olive,
+.lines-olive {
+	color: var(--olive);
+}
+
+.text-green,
+.line-green,
+.lines-green {
+	color: var(--green);
+}
+
+.text-cyan,
+.line-cyan,
+.lines-cyan {
+	color: var(--cyan);
+}
+
+.text-blue,
+.line-blue,
+.lines-blue {
+	color: var(--blue);
+}
+
+.text-purple,
+.line-purple,
+.lines-purple {
+	color: var(--purple);
+}
+
+.text-mauve,
+.line-mauve,
+.lines-mauve {
+	color: var(--mauve);
+}
+
+.text-pink,
+.line-pink,
+.lines-pink {
+	color: var(--pink);
+}
+
+.text-brown,
+.line-brown,
+.lines-brown {
+	color: var(--brown);
+}
+
+.text-grey,
+.line-grey,
+.lines-grey {
+	color: var(--grey);
+}
+
+.text-gray,
+.line-gray,
+.lines-gray {
+	color: var(--gray);
+}
+
+.text-black,
+.line-black,
+.lines-black {
+	color: var(--black);
+}
+
+.text-white,
+.line-white,
+.lines-white {
+	color: var(--white);
+}

+ 51 - 0
common.js

@@ -0,0 +1,51 @@
+const app = getApp()
+const appConfig = require('config')
+const login = require('js/login')
+const lists = require('js/lists')
+const genKeys = require('js/genKeys')
+const saveKeys = require('js/saveKeys')
+const savePwd = require("js/savePwd")
+const resetKeys = require("js/resetKeys")
+const info = require("js/info")
+const decrypt = require("js/decrypt")
+const verifyKeys = require("js/verifyKeys")
+const remove = require("js/remove")
+const saveClientPublicKey = require('js/saveClientPublicKey')
+
+var common = {
+  login: function(_this) {
+    login(_this)
+  },
+  lists: function(_this, page, pageSize) {
+    lists.pwd(_this, page, pageSize)
+  },
+  genKeys: function(_this) {
+    genKeys(_this);
+  },
+  saveKeys: function(_this) {
+    saveKeys(_this)
+  },
+  savePwd: function(_this, data) {
+    savePwd(_this, data);
+  },
+  resetKeys: function(_this, data) {
+    resetKeys(_this, data)
+  },
+  info: function(id) {
+    info(id);
+  },
+  decrypt: function(str, key) {
+    decrypt(str, key);
+  },
+  verifyKeys: function(publicKey, privateKey) {
+    verifyKeys(publicKey, privateKey);
+  },
+  remove: function(id, name) {
+    remove(id, name);
+  },
+  saveClientPublicKey: function(_this, publicKey, privateKey){
+    saveClientPublicKey(_this, publicKey, privateKey);
+  },
+}
+
+module.exports = common

+ 83 - 0
config.js

@@ -0,0 +1,83 @@
+//var host = "http://localhost/travelzs/public"
+var host = "https://www.travelzs.com"
+
+var config = {
+  host,
+  "api": {
+    "auth": `${host}/api/admin/password/auth.json`,
+    "login" : `${host}/api/admin/password/login.json`,
+    "lists": `${host}/api/admin/password/lists.json`,
+    "info" : `${host}/api/admin/password/info.json`,
+    "requestSslKey": `${host}/api/admin/password/genKeys.json`,
+    "savePublicKey": `${host}/api/admin/password/savePublicKey.json`,
+    "savePwd" : `${host}/api/admin/password/add.json`,
+    "remove" : `${host}/api/admin/password/del.json`,
+    "resetPublicKey": `${host}/api/admin/password/reset.json`,
+    "verifyKeys": `${host}/api/admin/password/verifyKeys.json`,
+    "decrypt": `${host}/api/admin/password/decrypt.json`,
+    "download" : `${host}/api/admin/password/download.json`,
+    "sync" : `${host}/api/admin/password/sync.json`,
+    "savePublicKeyClient": `${host}/api/admin/password/savePublicKeyClient.json`,
+    "batchUpdate": `${host}/api/admin/password/batchUpdate.json`
+  },
+  "storeKeys": {
+    "sslKeys": "sslKeys",
+    "serverInfo" : "serverInfo",
+    "sessionId": "sessionId",
+    "background": "background",
+    "password" : "password",
+    "switchToLocal": "switchToLocal",
+    "removedItem" : "removedItem",
+    "newItem" : "newItem",
+    "editItem" : "editItem",
+  },
+  "duration" : 1000,
+  "switchToLocal": wx.getStorageSync("switchToLocal") || 0,
+  "iconList": [{
+    icon: 'edit',
+    color: 'red',
+    badge: 0,
+    method : "add-password",
+    name: '创建我的密码'
+  }, {
+    icon: 'keyboard',
+    color: 'orange',
+    badge: 1,
+    method : "request-ssl-key",
+    name: '创建公私钥'
+    }, {
+      icon: 'refresh',
+      color: 'orange',
+      badge: 0,
+      method : "reset-ssl-key",
+      name: '重置私钥'
+    }],
+  "noPublicKeyIcon": [{
+    icon: 'edit',
+    color: 'red',
+    badge: 0,
+    method: "add-password",
+    name: '创建我的密码'
+  }, {
+      icon: 'refresh',
+      color: 'orange',
+      badge: 0,
+      method: "reset-ssl-key",
+      name: '重置私钥'
+    },
+    {
+      icon: 'pulldown',
+      color: 'orange',
+      badge: 0,
+      method: "save-to-local",
+      name: '同步数据'
+    },
+    ],
+    networkError:{
+      code: 100010,
+      msg: "网络链接失败,请重试"
+    },
+    pageSize: 50,
+}
+
+module.exports = config

BIN
images/logo.png


BIN
images/share.png


BIN
images/tabbar/about.png


BIN
images/tabbar/about_cur.png


BIN
images/tabbar/component.png


BIN
images/tabbar/component_cur.png


BIN
images/theme/10001.jpg


BIN
images/theme/10002.jpg


BIN
images/theme/10003.jpg


BIN
images/theme/10004.jpg


BIN
images/theme/10005.jpg


BIN
images/theme/10006.jpg


BIN
images/theme/10007.jpg


BIN
images/unlock.png


+ 347 - 0
js/cache.js

@@ -0,0 +1,347 @@
+const app = getApp();
+const appConfig = require("../config");
+const valid = require("../js/valid");
+const utils = require("../utils/util");
+
+var checkExist = function(val, data, filed) {
+  filed = filed || "id";
+  for (var i in data) {
+    if (val == data[i][filed]) {
+      return i;
+    }
+  }
+  return false;
+}
+var isSame = function(val, val2) {
+  if (val.length != val2.length) return false;
+  for (var i in val) {
+    if (val[i] != val2[i]) {
+      return false;
+    }
+  }
+  return true;
+}
+var addItem = function(item) {
+  //检查是否存在
+  var data = wx.getStorageSync(appConfig.storeKeys.password) || [];
+  var isExists = checkExist(item['name'], data, 'name');
+  if (isExists) {
+    app.warning("已经存在相关记录~");
+    return;
+  }
+  data.unshift(item);
+  wx.setStorageSync(appConfig.storeKeys.password, data);
+  //保存到新增item中
+  var newItem = wx.getStorageSync(appConfig.storeKeys.newItem) || [];
+  newItem.unshift(item);
+  wx.setStorageSync(appConfig.storeKeys.newItem, data);
+}
+var removeItemByName = function(id, name) {
+  //检查是否存在
+  var data = wx.getStorageSync(appConfig.storeKeys.password) || [];
+  var isExists = checkExist(name, data, 'name');
+
+  if (!isExists) {
+    app.warning("未找到相关记录");
+    return;
+  }
+  data.splice(isExists, 1);
+  wx.setStorageSync(appConfig.storeKeys.password, data);
+  if (id) {
+    //将删除的内容放到临时存储
+    var removedItem = wx.getStorageSync(appConfig.storeKeys.removedItem) || [];
+    removedItem.unshift(id);
+    wx.setStorageSync(appConfig.storeKeys.removedItem, removedItem);
+  }
+}
+var editLocal = function(item) {
+  var newItem = wx.getStorageSync(appConfig.storeKeys.newItem) || [];
+  var isFind = false;
+  for (var i in newItem) {
+    if (newItem[i]['name'] == item.name) {
+      newItem[i] = item;
+      isFind = true;
+    }
+  }
+  if (!isFind) {
+    newItem.unshift(item);
+  }
+  wx.setStorageSync(appConfig.storeKeys.newItem, newItem);
+}
+var editSingle = function(item) {
+  //检查是否存在
+  var data = wx.getStorageSync(appConfig.storeKeys.password) || [];
+  var isExists = checkExist(item['id'], data);
+  if (isExists) {
+    data[isExists] = item;
+  } else {
+    data.unshift(item);
+  }
+  wx.setStorageSync(appConfig.storeKeys.password, data);
+  //如果是本地编辑就将他放到
+  if (!item.id || !item.id == '') {
+    editLocal(item);
+  }
+}
+
+var info = function(id) {
+  var data = wx.getStorageSync(appConfig.storeKeys.password) || [];
+  for (var i in data) {
+    if (data[i]['id'] == id) {
+      return data[i];
+    }
+  }
+  return false;
+}
+
+var sync = function(data) {
+  if (!data) {
+    wx.showToast({
+      title: '参数不正确,请检查后重试!~',
+      duration: appConfig.duration
+    });
+    return;
+  }
+  if (!valid.minlength(data.name, 2)) {
+    app.warning("名称不能少于2个字符");
+    return;
+  }
+  if (!valid.minlength(data.account, 2)) {
+    app.warning("账户名称不能少于3个字符");
+    return;
+  }
+  if (!valid.minlength(data.password, 6)) {
+    app.warning("密码不能少于6个字符");
+    return;
+  }
+  if (!valid.isHttp(data.link)) {
+    app.warning("链接地址不正确");
+    return;
+  }
+  var header = {
+    'content-type': 'application/x-www-form-urlencoded',
+    'cookie': wx.getStorageSync(appConfig.storeKeys.sessionId)
+  };
+  wx.request({
+    url: utils.getUrl("sync"),
+    data: data,
+    header: header,
+    method: "POST",
+    dataType: "json",
+    success: function(res) {
+      if (typeof res.data == 'string') {
+        app.warning("同步数据失败~");
+        return;
+      }
+      app.syncSuccess(res.data);
+    },
+    fail: function(res) {
+      wx.showToast({
+        title: '保存失败,请重试',
+        duration: appConfig.duration
+      });
+      app.syncSuccess(appConfig.networkError);
+    }
+  });
+}
+
+var download = function(page, pageSize) {
+  wx.showLoading({
+    title: '正在同步',
+  });
+  var _this = this;
+  var header = {
+    'content-type': 'application/x-www-form-urlencoded',
+    'cookie': wx.getStorageSync(appConfig.storeKeys.sessionId)
+  };
+  //先检查本地是否有删除的数据
+  var removedItem = wx.getStorageSync(appConfig.storeKeys.removedItem) || [];
+  if (removedItem.length > 0) {
+    //批量删除
+    var id = removedItem.join(",");
+    wx.request({
+      url: utils.getUrl("remove"),
+      header: header,
+      data: {
+        ids: id
+      },
+      method: "POST",
+      dataType: "json",
+      success: function(req) {
+        wx.removeStorageSync(appConfig.storeKeys.removedItem);
+        download(page, pageSize);
+      },
+      fail: function(req) {
+        wx.hideLoading();
+        app.warning(appConfig.networkError.msg);
+      }
+    });
+    return;
+  }
+  //再检查本地是否有新增的数据
+  var newItem = wx.getStorageSync(appConfig.storeKeys.newItem) || [];
+  app.log(newItem);
+  if (newItem.length > 0) {
+    wx.request({
+      url: utils.getUrl('batchUpdate'),
+      header: header,
+      data: {
+        data: JSON.stringify(newItem)
+      },
+      dataType: 'json',
+      method: 'post',
+      success: function(res) {
+        app.log(res);
+        if (res.data) {
+          if (res.data.data) {
+            for (var i in res.data.data) {
+              var code = res.data.data[i]['code'];
+              var operate = res.data.data[i]['operate'] || "";
+              var data = res.data.data[i]['data'];
+              if (code == 0) {
+                //更新本地数据,并删除已经保存过的数据
+                if (operate == 'edit') {
+                  editSingle(data);
+                } else if (operate == 'add') {
+                  removeItemByName("", data.name);
+                }
+              }
+            }
+            wx.removeStorageSync(appConfig.storeKeys.newItem);
+            download(page, pageSize);
+          }
+        }
+      },
+      fail: function(res) {
+        wx.hideLoading();
+        app.warning(appConfig.networkError.msg);
+        if (app.syncRes) {
+          app.syncRes(appConfig.networkError);
+        }
+      }
+    })
+    return;
+  };
+  wx.request({
+    url: utils.getUrl("download", {
+      page: page,
+      pageSize: pageSize
+    }),
+    header: header,
+    dataType: 'json',
+    success: function(res) {
+      wx.hideLoading();
+      app.storeToLocal(res);
+      if (typeof res.data == 'string') {
+        app.warning("拉取数据失败~");
+        return;
+      }
+
+      var data = wx.getStorageSync(appConfig.storeKeys.password) || [];
+      if (data.length == 0) {
+        try {
+          wx.setStorageSync(appConfig.storeKeys.password, res.data.lists);
+        } catch (e) {}
+        if (app.syncRes) {
+          app.syncRes({
+            code: 0,
+            msg: "同步成功"
+          });
+        }
+        return;
+      }
+      var showFinish = true;
+      for (var i in res.data.lists) {
+        var isExist = checkExist(res.data.lists[i]['id'], data);
+        var _currentVal = res.data.lists[i];
+        if (isExist !== false) {
+          //如果存在的话, 数据不一致就提示用户是否更新
+          if (!isSame(data[isExist], res.data.lists[i])) {
+            showFinish = false;
+            wx.showModal({
+              title: '温馨提示',
+              content: '您本地保存账户' + data[isExist]['account'] + '信息与服务器上保存的不一致',
+              cancelText: "覆盖线上",
+              confirmText: "覆盖本地",
+              success: function(item) {
+                if (item.confirm) {
+                  //覆盖本地
+                  data[isExist] = _currentVal;
+                  try {
+                    wx.setStorageSync(appConfig.storeKeys.password, data);
+                    if (appConfig.switchToLocal) {
+                      _this.getFromLocal(1, appConfig.pageSize);
+                    }
+                  } catch (e) {}
+                  download(page, pageSize);
+                } else {
+                  //同步线上
+                  sync(data[isExist]);
+                }
+              }
+            });
+            return;
+          }
+        } else {
+          data.unshift(res.data.lists[i]);
+        }
+      }
+      try {
+        wx.setStorageSync(appConfig.storeKeys.password, data);
+        if (showFinish) {
+          if (appConfig.switchToLocal) {
+            _this.getFromLocal(1, appConfig.pageSize);
+          }
+          if (app.syncRes) {
+            app.syncRes({
+              code: 0,
+              msg: "同步成功"
+            });
+          }
+        }
+      } catch (e) {}
+    },
+    fail: function(res) {
+      wx.hideLoading();
+      app.warning(appConfig.networkError.msg);
+      if (app.syncRes) {
+        app.syncRes(appConfig.networkError);
+      }
+    }
+  });
+}
+var getFromLocal = function(page, pageSize) {
+  var pages = {
+    total: 0,
+    currentPage: 1,
+    totalPage: 0,
+    pageSize: parseInt(pageSize)
+  };
+  var start = (parseInt(page) - 1) * pageSize;
+  var end = pageSize;
+
+  var data = wx.getStorageSync(appConfig.storeKeys.password) || [];
+  //排序
+  data.sort(function(x, y) {
+    return parseInt(y['id']) - parseInt(x['id']);
+  });
+  pages.total = parseInt(data.length);
+  pages.totalPage = Math.ceil(pages.total / pages.pageSize);
+  pages.currentPage = Math.min(parseInt(page), pages.totalPage);
+  var lists = data.slice(start, end);
+
+  var res = {
+    pages: pages,
+    lists: lists
+  };
+  return res;
+}
+module.exports = {
+  info: info,
+  editSingle: editSingle,
+  download: download,
+  sync: download,
+  getFromLocal: getFromLocal,
+  addItem: addItem,
+  removeItemByName: removeItemByName,
+}

+ 36 - 0
js/decrypt.js

@@ -0,0 +1,36 @@
+const app = getApp();
+const appConfig = require("../config");
+const utils = require("../utils/util");
+
+var decrypt = function (str, privateKey) {
+  var header = {
+    'content-type': 'application/x-www-form-urlencoded',
+    'cookie': wx.getStorageSync(appConfig.storeKeys.sessionId)
+  };
+  wx.showLoading({
+    title: '正在解密',
+  });
+  wx.request({
+    url: utils.getUrl("decrypt"),
+    header: header,
+    data : {
+      val : str,
+      private_key : privateKey,
+    },
+    dataType: 'json',
+    method : "post",
+    success: function (res) {
+      wx.hideLoading();
+      app.sslKeyDecryptSync(res.data);
+    },
+    fail: function () {
+      wx.showToast({
+        title: '请求失败,请重试~',
+        duration: 3000
+      });
+      wx.hideLoading();
+    }
+  })
+}
+
+module.exports = decrypt

+ 36 - 0
js/genKeys.js

@@ -0,0 +1,36 @@
+const app = getApp();
+const appConfig = require("../config");
+const utils = require("../utils/util");
+
+var genKeys = function(that){
+  var header = {
+    'content-type': 'application/x-www-form-urlencoded',
+    'cookie': wx.getStorageSync(appConfig.storeKeys.sessionId)
+  };
+  wx.showLoading({
+    title: '正在拉取数据',
+  });
+  wx.request({
+    url: utils.getUrl("requestSslKey"),
+    header : header,
+    dataType : 'json',
+    success : function(res){
+      wx.hideLoading();
+        app.log(res)
+        if(res.data.code > 0) {
+          wx.showToast({
+            title: res.data.msg || "获取公私钥失败",
+            duration:appConfig.duration
+          });
+          return;
+        }
+        app.sslKeyRequestSync(res.data);
+    },
+    fail: function(){
+      app.sslKeyRequestSync(appConfig.networkError);
+      wx.hideLoading();
+    }
+  })
+}
+
+module.exports = genKeys

+ 45 - 0
js/info.js

@@ -0,0 +1,45 @@
+const app = getApp();
+const appConfig = require("../config");
+const utils = require("../utils/util");
+const cache = require("cache");
+
+var info = function (id) {
+  //如果用户切换成本地了,就从本低读取
+  if (utils.getSwitchToLocalStatus()){
+    var info = cache.info(id);
+    var res = {
+      code : 0,
+    };
+    if(info === false) {
+      res.code = 404;
+      res.msg = "没有找到相关记录";
+    }else{
+      res.data = info;
+    }
+    app.infoSuccessSync(res)
+    return res;
+  }
+  var header = {
+    'content-type': 'application/x-www-form-urlencoded',
+    'cookie': wx.getStorageSync(appConfig.storeKeys.sessionId)
+  };
+  wx.request({
+    url: utils.getUrl("info", {id : id}),
+    header : header,
+    method : "GET",
+    dataType : "json",
+    success : function(req){
+      app.log(req)
+      app.infoSuccessSync(req.data)
+    },
+    fail: function (req) {
+      var req = {
+        code: 100010,
+        msg: "网络失败,请重试"
+      };
+      app.infoSuccessSync(req)
+    }
+  })
+}
+
+module.exports = info

+ 5382 - 0
js/jsencrypt.js

@@ -0,0 +1,5382 @@
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+	typeof define === 'function' && define.amd ? define(['exports'], factory) :
+	(factory((global.JSEncrypt = {})));
+}(this, (function (exports) { 'use strict';
+  // 用来替换 navigator
+  var navigator2 = {
+    appName: 'Netscape',
+    userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'
+  };
+  //  用来替换window
+  var window2 = {
+    ASN1: null,
+    Base64: null,
+    Hex: null,
+    crypto: null,
+    href: null
+  };
+var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
+function int2char(n) {
+    return BI_RM.charAt(n);
+}
+//#region BIT_OPERATIONS
+// (public) this & a
+function op_and(x, y) {
+    return x & y;
+}
+// (public) this | a
+function op_or(x, y) {
+    return x | y;
+}
+// (public) this ^ a
+function op_xor(x, y) {
+    return x ^ y;
+}
+// (public) this & ~a
+function op_andnot(x, y) {
+    return x & ~y;
+}
+// return index of lowest 1-bit in x, x < 2^31
+function lbit(x) {
+    if (x == 0) {
+        return -1;
+    }
+    var r = 0;
+    if ((x & 0xffff) == 0) {
+        x >>= 16;
+        r += 16;
+    }
+    if ((x & 0xff) == 0) {
+        x >>= 8;
+        r += 8;
+    }
+    if ((x & 0xf) == 0) {
+        x >>= 4;
+        r += 4;
+    }
+    if ((x & 3) == 0) {
+        x >>= 2;
+        r += 2;
+    }
+    if ((x & 1) == 0) {
+        ++r;
+    }
+    return r;
+}
+// return number of 1 bits in x
+function cbit(x) {
+    var r = 0;
+    while (x != 0) {
+        x &= x - 1;
+        ++r;
+    }
+    return r;
+}
+//#endregion BIT_OPERATIONS
+
+var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+var b64pad = "=";
+function hex2b64(h) {
+    var i;
+    var c;
+    var ret = "";
+    for (i = 0; i + 3 <= h.length; i += 3) {
+        c = parseInt(h.substring(i, i + 3), 16);
+        ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
+    }
+    if (i + 1 == h.length) {
+        c = parseInt(h.substring(i, i + 1), 16);
+        ret += b64map.charAt(c << 2);
+    }
+    else if (i + 2 == h.length) {
+        c = parseInt(h.substring(i, i + 2), 16);
+        ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
+    }
+    while ((ret.length & 3) > 0) {
+        ret += b64pad;
+    }
+    return ret;
+}
+// convert a base64 string to hex
+function b64tohex(s) {
+    var ret = "";
+    var i;
+    var k = 0; // b64 state, 0-3
+    var slop = 0;
+    for (i = 0; i < s.length; ++i) {
+        if (s.charAt(i) == b64pad) {
+            break;
+        }
+        var v = b64map.indexOf(s.charAt(i));
+        if (v < 0) {
+            continue;
+        }
+        if (k == 0) {
+            ret += int2char(v >> 2);
+            slop = v & 3;
+            k = 1;
+        }
+        else if (k == 1) {
+            ret += int2char((slop << 2) | (v >> 4));
+            slop = v & 0xf;
+            k = 2;
+        }
+        else if (k == 2) {
+            ret += int2char(slop);
+            ret += int2char(v >> 2);
+            slop = v & 3;
+            k = 3;
+        }
+        else {
+            ret += int2char((slop << 2) | (v >> 4));
+            ret += int2char(v & 0xf);
+            k = 0;
+        }
+    }
+    if (k == 1) {
+        ret += int2char(slop << 2);
+    }
+    return ret;
+}
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+// Hex JavaScript decoder
+// Copyright (c) 2008-2013 Lapo Luchini <lapo@lapo.it>
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+/*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */
+var decoder;
+var Hex = {
+    decode: function (a) {
+        var i;
+        if (decoder === undefined) {
+            var hex = "0123456789ABCDEF";
+            var ignore = " \f\n\r\t\u00A0\u2028\u2029";
+            decoder = {};
+            for (i = 0; i < 16; ++i) {
+                decoder[hex.charAt(i)] = i;
+            }
+            hex = hex.toLowerCase();
+            for (i = 10; i < 16; ++i) {
+                decoder[hex.charAt(i)] = i;
+            }
+            for (i = 0; i < ignore.length; ++i) {
+                decoder[ignore.charAt(i)] = -1;
+            }
+        }
+        var out = [];
+        var bits = 0;
+        var char_count = 0;
+        for (i = 0; i < a.length; ++i) {
+            var c = a.charAt(i);
+            if (c == "=") {
+                break;
+            }
+            c = decoder[c];
+            if (c == -1) {
+                continue;
+            }
+            if (c === undefined) {
+                throw new Error("Illegal character at offset " + i);
+            }
+            bits |= c;
+            if (++char_count >= 2) {
+                out[out.length] = bits;
+                bits = 0;
+                char_count = 0;
+            }
+            else {
+                bits <<= 4;
+            }
+        }
+        if (char_count) {
+            throw new Error("Hex encoding incomplete: 4 bits missing");
+        }
+        return out;
+    }
+};
+
+// Base64 JavaScript decoder
+// Copyright (c) 2008-2013 Lapo Luchini <lapo@lapo.it>
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+/*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */
+var decoder$1;
+var Base64 = {
+    decode: function (a) {
+        var i;
+        if (decoder$1 === undefined) {
+            var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+            var ignore = "= \f\n\r\t\u00A0\u2028\u2029";
+            decoder$1 = Object.create(null);
+            for (i = 0; i < 64; ++i) {
+                decoder$1[b64.charAt(i)] = i;
+            }
+            for (i = 0; i < ignore.length; ++i) {
+                decoder$1[ignore.charAt(i)] = -1;
+            }
+        }
+        var out = [];
+        var bits = 0;
+        var char_count = 0;
+        for (i = 0; i < a.length; ++i) {
+            var c = a.charAt(i);
+            if (c == "=") {
+                break;
+            }
+            c = decoder$1[c];
+            if (c == -1) {
+                continue;
+            }
+            if (c === undefined) {
+                throw new Error("Illegal character at offset " + i);
+            }
+            bits |= c;
+            if (++char_count >= 4) {
+                out[out.length] = (bits >> 16);
+                out[out.length] = (bits >> 8) & 0xFF;
+                out[out.length] = bits & 0xFF;
+                bits = 0;
+                char_count = 0;
+            }
+            else {
+                bits <<= 6;
+            }
+        }
+        switch (char_count) {
+            case 1:
+                throw new Error("Base64 encoding incomplete: at least 2 bits missing");
+            case 2:
+                out[out.length] = (bits >> 10);
+                break;
+            case 3:
+                out[out.length] = (bits >> 16);
+                out[out.length] = (bits >> 8) & 0xFF;
+                break;
+        }
+        return out;
+    },
+    re: /-----BEGIN [^-]+-----([A-Za-z0-9+\/=\s]+)-----END [^-]+-----|begin-base64[^\n]+\n([A-Za-z0-9+\/=\s]+)====/,
+    unarmor: function (a) {
+        var m = Base64.re.exec(a);
+        if (m) {
+            if (m[1]) {
+                a = m[1];
+            }
+            else if (m[2]) {
+                a = m[2];
+            }
+            else {
+                throw new Error("RegExp out of sync");
+            }
+        }
+        return Base64.decode(a);
+    }
+};
+
+// Big integer base-10 printing library
+// Copyright (c) 2014 Lapo Luchini <lapo@lapo.it>
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+/*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */
+var max = 10000000000000; // biggest integer that can still fit 2^53 when multiplied by 256
+var Int10 = /** @class */ (function () {
+    function Int10(value) {
+        this.buf = [+value || 0];
+    }
+    Int10.prototype.mulAdd = function (m, c) {
+        // assert(m <= 256)
+        var b = this.buf;
+        var l = b.length;
+        var i;
+        var t;
+        for (i = 0; i < l; ++i) {
+            t = b[i] * m + c;
+            if (t < max) {
+                c = 0;
+            }
+            else {
+                c = 0 | (t / max);
+                t -= c * max;
+            }
+            b[i] = t;
+        }
+        if (c > 0) {
+            b[i] = c;
+        }
+    };
+    Int10.prototype.sub = function (c) {
+        // assert(m <= 256)
+        var b = this.buf;
+        var l = b.length;
+        var i;
+        var t;
+        for (i = 0; i < l; ++i) {
+            t = b[i] - c;
+            if (t < 0) {
+                t += max;
+                c = 1;
+            }
+            else {
+                c = 0;
+            }
+            b[i] = t;
+        }
+        while (b[b.length - 1] === 0) {
+            b.pop();
+        }
+    };
+    Int10.prototype.toString = function (base) {
+        if ((base || 10) != 10) {
+            throw new Error("only base 10 is supported");
+        }
+        var b = this.buf;
+        var s = b[b.length - 1].toString();
+        for (var i = b.length - 2; i >= 0; --i) {
+            s += (max + b[i]).toString().substring(1);
+        }
+        return s;
+    };
+    Int10.prototype.valueOf = function () {
+        var b = this.buf;
+        var v = 0;
+        for (var i = b.length - 1; i >= 0; --i) {
+            v = v * max + b[i];
+        }
+        return v;
+    };
+    Int10.prototype.simplify = function () {
+        var b = this.buf;
+        return (b.length == 1) ? b[0] : this;
+    };
+    return Int10;
+}());
+
+// ASN.1 JavaScript decoder
+var ellipsis = "\u2026";
+var reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/;
+var reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/;
+function stringCut(str, len) {
+    if (str.length > len) {
+        str = str.substring(0, len) + ellipsis;
+    }
+    return str;
+}
+var Stream = /** @class */ (function () {
+    function Stream(enc, pos) {
+        this.hexDigits = "0123456789ABCDEF";
+        if (enc instanceof Stream) {
+            this.enc = enc.enc;
+            this.pos = enc.pos;
+        }
+        else {
+            // enc should be an array or a binary string
+            this.enc = enc;
+            this.pos = pos;
+        }
+    }
+    Stream.prototype.get = function (pos) {
+        if (pos === undefined) {
+            pos = this.pos++;
+        }
+        if (pos >= this.enc.length) {
+            throw new Error("Requesting byte offset " + pos + " on a stream of length " + this.enc.length);
+        }
+        return ("string" === typeof this.enc) ? this.enc.charCodeAt(pos) : this.enc[pos];
+    };
+    Stream.prototype.hexByte = function (b) {
+        return this.hexDigits.charAt((b >> 4) & 0xF) + this.hexDigits.charAt(b & 0xF);
+    };
+    Stream.prototype.hexDump = function (start, end, raw) {
+        var s = "";
+        for (var i = start; i < end; ++i) {
+            s += this.hexByte(this.get(i));
+            if (raw !== true) {
+                switch (i & 0xF) {
+                    case 0x7:
+                        s += "  ";
+                        break;
+                    case 0xF:
+                        s += "\n";
+                        break;
+                    default:
+                        s += " ";
+                }
+            }
+        }
+        return s;
+    };
+    Stream.prototype.isASCII = function (start, end) {
+        for (var i = start; i < end; ++i) {
+            var c = this.get(i);
+            if (c < 32 || c > 176) {
+                return false;
+            }
+        }
+        return true;
+    };
+    Stream.prototype.parseStringISO = function (start, end) {
+        var s = "";
+        for (var i = start; i < end; ++i) {
+            s += String.fromCharCode(this.get(i));
+        }
+        return s;
+    };
+    Stream.prototype.parseStringUTF = function (start, end) {
+        var s = "";
+        for (var i = start; i < end;) {
+            var c = this.get(i++);
+            if (c < 128) {
+                s += String.fromCharCode(c);
+            }
+            else if ((c > 191) && (c < 224)) {
+                s += String.fromCharCode(((c & 0x1F) << 6) | (this.get(i++) & 0x3F));
+            }
+            else {
+                s += String.fromCharCode(((c & 0x0F) << 12) | ((this.get(i++) & 0x3F) << 6) | (this.get(i++) & 0x3F));
+            }
+        }
+        return s;
+    };
+    Stream.prototype.parseStringBMP = function (start, end) {
+        var str = "";
+        var hi;
+        var lo;
+        for (var i = start; i < end;) {
+            hi = this.get(i++);
+            lo = this.get(i++);
+            str += String.fromCharCode((hi << 8) | lo);
+        }
+        return str;
+    };
+    Stream.prototype.parseTime = function (start, end, shortYear) {
+        var s = this.parseStringISO(start, end);
+        var m = (shortYear ? reTimeS : reTimeL).exec(s);
+        if (!m) {
+            return "Unrecognized time: " + s;
+        }
+        if (shortYear) {
+            // to avoid querying the timer, use the fixed range [1970, 2069]
+            // it will conform with ITU X.400 [-10, +40] sliding window until 2030
+            m[1] = +m[1];
+            m[1] += (+m[1] < 70) ? 2000 : 1900;
+        }
+        s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4];
+        if (m[5]) {
+            s += ":" + m[5];
+            if (m[6]) {
+                s += ":" + m[6];
+                if (m[7]) {
+                    s += "." + m[7];
+                }
+            }
+        }
+        if (m[8]) {
+            s += " UTC";
+            if (m[8] != "Z") {
+                s += m[8];
+                if (m[9]) {
+                    s += ":" + m[9];
+                }
+            }
+        }
+        return s;
+    };
+    Stream.prototype.parseInteger = function (start, end) {
+        var v = this.get(start);
+        var neg = (v > 127);
+        var pad = neg ? 255 : 0;
+        var len;
+        var s = "";
+        // skip unuseful bits (not allowed in DER)
+        while (v == pad && ++start < end) {
+            v = this.get(start);
+        }
+        len = end - start;
+        if (len === 0) {
+            return neg ? -1 : 0;
+        }
+        // show bit length of huge integers
+        if (len > 4) {
+            s = v;
+            len <<= 3;
+            while (((+s ^ pad) & 0x80) == 0) {
+                s = +s << 1;
+                --len;
+            }
+            s = "(" + len + " bit)\n";
+        }
+        // decode the integer
+        if (neg) {
+            v = v - 256;
+        }
+        var n = new Int10(v);
+        for (var i = start + 1; i < end; ++i) {
+            n.mulAdd(256, this.get(i));
+        }
+        return s + n.toString();
+    };
+    Stream.prototype.parseBitString = function (start, end, maxLength) {
+        var unusedBit = this.get(start);
+        var lenBit = ((end - start - 1) << 3) - unusedBit;
+        var intro = "(" + lenBit + " bit)\n";
+        var s = "";
+        for (var i = start + 1; i < end; ++i) {
+            var b = this.get(i);
+            var skip = (i == end - 1) ? unusedBit : 0;
+            for (var j = 7; j >= skip; --j) {
+                s += (b >> j) & 1 ? "1" : "0";
+            }
+            if (s.length > maxLength) {
+                return intro + stringCut(s, maxLength);
+            }
+        }
+        return intro + s;
+    };
+    Stream.prototype.parseOctetString = function (start, end, maxLength) {
+        if (this.isASCII(start, end)) {
+            return stringCut(this.parseStringISO(start, end), maxLength);
+        }
+        var len = end - start;
+        var s = "(" + len + " byte)\n";
+        maxLength /= 2; // we work in bytes
+        if (len > maxLength) {
+            end = start + maxLength;
+        }
+        for (var i = start; i < end; ++i) {
+            s += this.hexByte(this.get(i));
+        }
+        if (len > maxLength) {
+            s += ellipsis;
+        }
+        return s;
+    };
+    Stream.prototype.parseOID = function (start, end, maxLength) {
+        var s = "";
+        var n = new Int10();
+        var bits = 0;
+        for (var i = start; i < end; ++i) {
+            var v = this.get(i);
+            n.mulAdd(128, v & 0x7F);
+            bits += 7;
+            if (!(v & 0x80)) { // finished
+                if (s === "") {
+                    n = n.simplify();
+                    if (n instanceof Int10) {
+                        n.sub(80);
+                        s = "2." + n.toString();
+                    }
+                    else {
+                        var m = n < 80 ? n < 40 ? 0 : 1 : 2;
+                        s = m + "." + (n - m * 40);
+                    }
+                }
+                else {
+                    s += "." + n.toString();
+                }
+                if (s.length > maxLength) {
+                    return stringCut(s, maxLength);
+                }
+                n = new Int10();
+                bits = 0;
+            }
+        }
+        if (bits > 0) {
+            s += ".incomplete";
+        }
+        return s;
+    };
+    return Stream;
+}());
+var ASN1 = /** @class */ (function () {
+    function ASN1(stream, header, length, tag, sub) {
+        if (!(tag instanceof ASN1Tag)) {
+            throw new Error("Invalid tag value.");
+        }
+        this.stream = stream;
+        this.header = header;
+        this.length = length;
+        this.tag = tag;
+        this.sub = sub;
+    }
+    ASN1.prototype.typeName = function () {
+        switch (this.tag.tagClass) {
+            case 0: // universal
+                switch (this.tag.tagNumber) {
+                    case 0x00:
+                        return "EOC";
+                    case 0x01:
+                        return "BOOLEAN";
+                    case 0x02:
+                        return "INTEGER";
+                    case 0x03:
+                        return "BIT_STRING";
+                    case 0x04:
+                        return "OCTET_STRING";
+                    case 0x05:
+                        return "NULL";
+                    case 0x06:
+                        return "OBJECT_IDENTIFIER";
+                    case 0x07:
+                        return "ObjectDescriptor";
+                    case 0x08:
+                        return "EXTERNAL";
+                    case 0x09:
+                        return "REAL";
+                    case 0x0A:
+                        return "ENUMERATED";
+                    case 0x0B:
+                        return "EMBEDDED_PDV";
+                    case 0x0C:
+                        return "UTF8String";
+                    case 0x10:
+                        return "SEQUENCE";
+                    case 0x11:
+                        return "SET";
+                    case 0x12:
+                        return "NumericString";
+                    case 0x13:
+                        return "PrintableString"; // ASCII subset
+                    case 0x14:
+                        return "TeletexString"; // aka T61String
+                    case 0x15:
+                        return "VideotexString";
+                    case 0x16:
+                        return "IA5String"; // ASCII
+                    case 0x17:
+                        return "UTCTime";
+                    case 0x18:
+                        return "GeneralizedTime";
+                    case 0x19:
+                        return "GraphicString";
+                    case 0x1A:
+                        return "VisibleString"; // ASCII subset
+                    case 0x1B:
+                        return "GeneralString";
+                    case 0x1C:
+                        return "UniversalString";
+                    case 0x1E:
+                        return "BMPString";
+                }
+                return "Universal_" + this.tag.tagNumber.toString();
+            case 1:
+                return "Application_" + this.tag.tagNumber.toString();
+            case 2:
+                return "[" + this.tag.tagNumber.toString() + "]"; // Context
+            case 3:
+                return "Private_" + this.tag.tagNumber.toString();
+        }
+    };
+    ASN1.prototype.content = function (maxLength) {
+        if (this.tag === undefined) {
+            return null;
+        }
+        if (maxLength === undefined) {
+            maxLength = Infinity;
+        }
+        var content = this.posContent();
+        var len = Math.abs(this.length);
+        if (!this.tag.isUniversal()) {
+            if (this.sub !== null) {
+                return "(" + this.sub.length + " elem)";
+            }
+            return this.stream.parseOctetString(content, content + len, maxLength);
+        }
+        switch (this.tag.tagNumber) {
+            case 0x01: // BOOLEAN
+                return (this.stream.get(content) === 0) ? "false" : "true";
+            case 0x02: // INTEGER
+                return this.stream.parseInteger(content, content + len);
+            case 0x03: // BIT_STRING
+                return this.sub ? "(" + this.sub.length + " elem)" :
+                    this.stream.parseBitString(content, content + len, maxLength);
+            case 0x04: // OCTET_STRING
+                return this.sub ? "(" + this.sub.length + " elem)" :
+                    this.stream.parseOctetString(content, content + len, maxLength);
+            // case 0x05: // NULL
+            case 0x06: // OBJECT_IDENTIFIER
+                return this.stream.parseOID(content, content + len, maxLength);
+            // case 0x07: // ObjectDescriptor
+            // case 0x08: // EXTERNAL
+            // case 0x09: // REAL
+            // case 0x0A: // ENUMERATED
+            // case 0x0B: // EMBEDDED_PDV
+            case 0x10: // SEQUENCE
+            case 0x11: // SET
+                if (this.sub !== null) {
+                    return "(" + this.sub.length + " elem)";
+                }
+                else {
+                    return "(no elem)";
+                }
+            case 0x0C: // UTF8String
+                return stringCut(this.stream.parseStringUTF(content, content + len), maxLength);
+            case 0x12: // NumericString
+            case 0x13: // PrintableString
+            case 0x14: // TeletexString
+            case 0x15: // VideotexString
+            case 0x16: // IA5String
+            // case 0x19: // GraphicString
+            case 0x1A: // VisibleString
+                // case 0x1B: // GeneralString
+                // case 0x1C: // UniversalString
+                return stringCut(this.stream.parseStringISO(content, content + len), maxLength);
+            case 0x1E: // BMPString
+                return stringCut(this.stream.parseStringBMP(content, content + len), maxLength);
+            case 0x17: // UTCTime
+            case 0x18: // GeneralizedTime
+                return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17));
+        }
+        return null;
+    };
+    ASN1.prototype.toString = function () {
+        return this.typeName() + "@" + this.stream.pos + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.sub === null) ? "null" : this.sub.length) + "]";
+    };
+    ASN1.prototype.toPrettyString = function (indent) {
+        if (indent === undefined) {
+            indent = "";
+        }
+        var s = indent + this.typeName() + " @" + this.stream.pos;
+        if (this.length >= 0) {
+            s += "+";
+        }
+        s += this.length;
+        if (this.tag.tagConstructed) {
+            s += " (constructed)";
+        }
+        else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null)) {
+            s += " (encapsulates)";
+        }
+        s += "\n";
+        if (this.sub !== null) {
+            indent += "  ";
+            for (var i = 0, max = this.sub.length; i < max; ++i) {
+                s += this.sub[i].toPrettyString(indent);
+            }
+        }
+        return s;
+    };
+    ASN1.prototype.posStart = function () {
+        return this.stream.pos;
+    };
+    ASN1.prototype.posContent = function () {
+        return this.stream.pos + this.header;
+    };
+    ASN1.prototype.posEnd = function () {
+        return this.stream.pos + this.header + Math.abs(this.length);
+    };
+    ASN1.prototype.toHexString = function () {
+        return this.stream.hexDump(this.posStart(), this.posEnd(), true);
+    };
+    ASN1.decodeLength = function (stream) {
+        var buf = stream.get();
+        var len = buf & 0x7F;
+        if (len == buf) {
+            return len;
+        }
+        // no reason to use Int10, as it would be a huge buffer anyways
+        if (len > 6) {
+            throw new Error("Length over 48 bits not supported at position " + (stream.pos - 1));
+        }
+        if (len === 0) {
+            return null;
+        } // undefined
+        buf = 0;
+        for (var i = 0; i < len; ++i) {
+            buf = (buf * 256) + stream.get();
+        }
+        return buf;
+    };
+    /**
+     * Retrieve the hexadecimal value (as a string) of the current ASN.1 element
+     * @returns {string}
+     * @public
+     */
+    ASN1.prototype.getHexStringValue = function () {
+        var hexString = this.toHexString();
+        var offset = this.header * 2;
+        var length = this.length * 2;
+        return hexString.substr(offset, length);
+    };
+    ASN1.decode = function (str) {
+        var stream;
+        if (!(str instanceof Stream)) {
+            stream = new Stream(str, 0);
+        }
+        else {
+            stream = str;
+        }
+        var streamStart = new Stream(stream);
+        var tag = new ASN1Tag(stream);
+        var len = ASN1.decodeLength(stream);
+        var start = stream.pos;
+        var header = start - streamStart.pos;
+        var sub = null;
+        var getSub = function () {
+            var ret = [];
+            if (len !== null) {
+                // definite length
+                var end = start + len;
+                while (stream.pos < end) {
+                    ret[ret.length] = ASN1.decode(stream);
+                }
+                if (stream.pos != end) {
+                    throw new Error("Content size is not correct for container starting at offset " + start);
+                }
+            }
+            else {
+                // undefined length
+                try {
+                    for (;;) {
+                        var s = ASN1.decode(stream);
+                        if (s.tag.isEOC()) {
+                            break;
+                        }
+                        ret[ret.length] = s;
+                    }
+                    len = start - stream.pos; // undefined lengths are represented as negative values
+                }
+                catch (e) {
+                    throw new Error("Exception while decoding undefined length content: " + e);
+                }
+            }
+            return ret;
+        };
+        if (tag.tagConstructed) {
+            // must have valid content
+            sub = getSub();
+        }
+        else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) {
+            // sometimes BitString and OctetString are used to encapsulate ASN.1
+            try {
+                if (tag.tagNumber == 0x03) {
+                    if (stream.get() != 0) {
+                        throw new Error("BIT STRINGs with unused bits cannot encapsulate.");
+                    }
+                }
+                sub = getSub();
+                for (var i = 0; i < sub.length; ++i) {
+                    if (sub[i].tag.isEOC()) {
+                        throw new Error("EOC is not supposed to be actual content.");
+                    }
+                }
+            }
+            catch (e) {
+                // but silently ignore when they don't
+                sub = null;
+            }
+        }
+        if (sub === null) {
+            if (len === null) {
+                throw new Error("We can't skip over an invalid tag with undefined length at offset " + start);
+            }
+            stream.pos = start + Math.abs(len);
+        }
+        return new ASN1(streamStart, header, len, tag, sub);
+    };
+    return ASN1;
+}());
+var ASN1Tag = /** @class */ (function () {
+    function ASN1Tag(stream) {
+        var buf = stream.get();
+        this.tagClass = buf >> 6;
+        this.tagConstructed = ((buf & 0x20) !== 0);
+        this.tagNumber = buf & 0x1F;
+        if (this.tagNumber == 0x1F) { // long tag
+            var n = new Int10();
+            do {
+                buf = stream.get();
+                n.mulAdd(128, buf & 0x7F);
+            } while (buf & 0x80);
+            this.tagNumber = n.simplify();
+        }
+    }
+    ASN1Tag.prototype.isUniversal = function () {
+        return this.tagClass === 0x00;
+    };
+    ASN1Tag.prototype.isEOC = function () {
+        return this.tagClass === 0x00 && this.tagNumber === 0x00;
+    };
+    return ASN1Tag;
+}());
+
+// Copyright (c) 2005  Tom Wu
+// Bits per digit
+var dbits;
+// JavaScript engine analysis
+var canary = 0xdeadbeefcafe;
+var j_lm = ((canary & 0xffffff) == 0xefcafe);
+//#region
+var lowprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997];
+var lplim = (1 << 26) / lowprimes[lowprimes.length - 1];
+//#endregion
+// (public) Constructor
+var BigInteger = /** @class */ (function () {
+    function BigInteger(a, b, c) {
+        if (a != null) {
+            if ("number" == typeof a) {
+                this.fromNumber(a, b, c);
+            }
+            else if (b == null && "string" != typeof a) {
+                this.fromString(a, 256);
+            }
+            else {
+                this.fromString(a, b);
+            }
+        }
+    }
+    //#region PUBLIC
+    // BigInteger.prototype.toString = bnToString;
+    // (public) return string representation in given radix
+    BigInteger.prototype.toString = function (b) {
+        if (this.s < 0) {
+            return "-" + this.negate().toString(b);
+        }
+        var k;
+        if (b == 16) {
+            k = 4;
+        }
+        else if (b == 8) {
+            k = 3;
+        }
+        else if (b == 2) {
+            k = 1;
+        }
+        else if (b == 32) {
+            k = 5;
+        }
+        else if (b == 4) {
+            k = 2;
+        }
+        else {
+            return this.toRadix(b);
+        }
+        var km = (1 << k) - 1;
+        var d;
+        var m = false;
+        var r = "";
+        var i = this.t;
+        var p = this.DB - (i * this.DB) % k;
+        if (i-- > 0) {
+            if (p < this.DB && (d = this[i] >> p) > 0) {
+                m = true;
+                r = int2char(d);
+            }
+            while (i >= 0) {
+                if (p < k) {
+                    d = (this[i] & ((1 << p) - 1)) << (k - p);
+                    d |= this[--i] >> (p += this.DB - k);
+                }
+                else {
+                    d = (this[i] >> (p -= k)) & km;
+                    if (p <= 0) {
+                        p += this.DB;
+                        --i;
+                    }
+                }
+                if (d > 0) {
+                    m = true;
+                }
+                if (m) {
+                    r += int2char(d);
+                }
+            }
+        }
+        return m ? r : "0";
+    };
+    // BigInteger.prototype.negate = bnNegate;
+    // (public) -this
+    BigInteger.prototype.negate = function () {
+        var r = nbi();
+        BigInteger.ZERO.subTo(this, r);
+        return r;
+    };
+    // BigInteger.prototype.abs = bnAbs;
+    // (public) |this|
+    BigInteger.prototype.abs = function () {
+        return (this.s < 0) ? this.negate() : this;
+    };
+    // BigInteger.prototype.compareTo = bnCompareTo;
+    // (public) return + if this > a, - if this < a, 0 if equal
+    BigInteger.prototype.compareTo = function (a) {
+        var r = this.s - a.s;
+        if (r != 0) {
+            return r;
+        }
+        var i = this.t;
+        r = i - a.t;
+        if (r != 0) {
+            return (this.s < 0) ? -r : r;
+        }
+        while (--i >= 0) {
+            if ((r = this[i] - a[i]) != 0) {
+                return r;
+            }
+        }
+        return 0;
+    };
+    // BigInteger.prototype.bitLength = bnBitLength;
+    // (public) return the number of bits in "this"
+    BigInteger.prototype.bitLength = function () {
+        if (this.t <= 0) {
+            return 0;
+        }
+        return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM));
+    };
+    // BigInteger.prototype.mod = bnMod;
+    // (public) this mod a
+    BigInteger.prototype.mod = function (a) {
+        var r = nbi();
+        this.abs().divRemTo(a, null, r);
+        if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) {
+            a.subTo(r, r);
+        }
+        return r;
+    };
+    // BigInteger.prototype.modPowInt = bnModPowInt;
+    // (public) this^e % m, 0 <= e < 2^32
+    BigInteger.prototype.modPowInt = function (e, m) {
+        var z;
+        if (e < 256 || m.isEven()) {
+            z = new Classic(m);
+        }
+        else {
+            z = new Montgomery(m);
+        }
+        return this.exp(e, z);
+    };
+    // BigInteger.prototype.clone = bnClone;
+    // (public)
+    BigInteger.prototype.clone = function () {
+        var r = nbi();
+        this.copyTo(r);
+        return r;
+    };
+    // BigInteger.prototype.intValue = bnIntValue;
+    // (public) return value as integer
+    BigInteger.prototype.intValue = function () {
+        if (this.s < 0) {
+            if (this.t == 1) {
+                return this[0] - this.DV;
+            }
+            else if (this.t == 0) {
+                return -1;
+            }
+        }
+        else if (this.t == 1) {
+            return this[0];
+        }
+        else if (this.t == 0) {
+            return 0;
+        }
+        // assumes 16 < DB < 32
+        return ((this[1] & ((1 << (32 - this.DB)) - 1)) << this.DB) | this[0];
+    };
+    // BigInteger.prototype.byteValue = bnByteValue;
+    // (public) return value as byte
+    BigInteger.prototype.byteValue = function () {
+        return (this.t == 0) ? this.s : (this[0] << 24) >> 24;
+    };
+    // BigInteger.prototype.shortValue = bnShortValue;
+    // (public) return value as short (assumes DB>=16)
+    BigInteger.prototype.shortValue = function () {
+        return (this.t == 0) ? this.s : (this[0] << 16) >> 16;
+    };
+    // BigInteger.prototype.signum = bnSigNum;
+    // (public) 0 if this == 0, 1 if this > 0
+    BigInteger.prototype.signum = function () {
+        if (this.s < 0) {
+            return -1;
+        }
+        else if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) {
+            return 0;
+        }
+        else {
+            return 1;
+        }
+    };
+    // BigInteger.prototype.toByteArray = bnToByteArray;
+    // (public) convert to bigendian byte array
+    BigInteger.prototype.toByteArray = function () {
+        var i = this.t;
+        var r = [];
+        r[0] = this.s;
+        var p = this.DB - (i * this.DB) % 8;
+        var d;
+        var k = 0;
+        if (i-- > 0) {
+            if (p < this.DB && (d = this[i] >> p) != (this.s & this.DM) >> p) {
+                r[k++] = d | (this.s << (this.DB - p));
+            }
+            while (i >= 0) {
+                if (p < 8) {
+                    d = (this[i] & ((1 << p) - 1)) << (8 - p);
+                    d |= this[--i] >> (p += this.DB - 8);
+                }
+                else {
+                    d = (this[i] >> (p -= 8)) & 0xff;
+                    if (p <= 0) {
+                        p += this.DB;
+                        --i;
+                    }
+                }
+                if ((d & 0x80) != 0) {
+                    d |= -256;
+                }
+                if (k == 0 && (this.s & 0x80) != (d & 0x80)) {
+                    ++k;
+                }
+                if (k > 0 || d != this.s) {
+                    r[k++] = d;
+                }
+            }
+        }
+        return r;
+    };
+    // BigInteger.prototype.equals = bnEquals;
+    BigInteger.prototype.equals = function (a) {
+        return (this.compareTo(a) == 0);
+    };
+    // BigInteger.prototype.min = bnMin;
+    BigInteger.prototype.min = function (a) {
+        return (this.compareTo(a) < 0) ? this : a;
+    };
+    // BigInteger.prototype.max = bnMax;
+    BigInteger.prototype.max = function (a) {
+        return (this.compareTo(a) > 0) ? this : a;
+    };
+    // BigInteger.prototype.and = bnAnd;
+    BigInteger.prototype.and = function (a) {
+        var r = nbi();
+        this.bitwiseTo(a, op_and, r);
+        return r;
+    };
+    // BigInteger.prototype.or = bnOr;
+    BigInteger.prototype.or = function (a) {
+        var r = nbi();
+        this.bitwiseTo(a, op_or, r);
+        return r;
+    };
+    // BigInteger.prototype.xor = bnXor;
+    BigInteger.prototype.xor = function (a) {
+        var r = nbi();
+        this.bitwiseTo(a, op_xor, r);
+        return r;
+    };
+    // BigInteger.prototype.andNot = bnAndNot;
+    BigInteger.prototype.andNot = function (a) {
+        var r = nbi();
+        this.bitwiseTo(a, op_andnot, r);
+        return r;
+    };
+    // BigInteger.prototype.not = bnNot;
+    // (public) ~this
+    BigInteger.prototype.not = function () {
+        var r = nbi();
+        for (var i = 0; i < this.t; ++i) {
+            r[i] = this.DM & ~this[i];
+        }
+        r.t = this.t;
+        r.s = ~this.s;
+        return r;
+    };
+    // BigInteger.prototype.shiftLeft = bnShiftLeft;
+    // (public) this << n
+    BigInteger.prototype.shiftLeft = function (n) {
+        var r = nbi();
+        if (n < 0) {
+            this.rShiftTo(-n, r);
+        }
+        else {
+            this.lShiftTo(n, r);
+        }
+        return r;
+    };
+    // BigInteger.prototype.shiftRight = bnShiftRight;
+    // (public) this >> n
+    BigInteger.prototype.shiftRight = function (n) {
+        var r = nbi();
+        if (n < 0) {
+            this.lShiftTo(-n, r);
+        }
+        else {
+            this.rShiftTo(n, r);
+        }
+        return r;
+    };
+    // BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;
+    // (public) returns index of lowest 1-bit (or -1 if none)
+    BigInteger.prototype.getLowestSetBit = function () {
+        for (var i = 0; i < this.t; ++i) {
+            if (this[i] != 0) {
+                return i * this.DB + lbit(this[i]);
+            }
+        }
+        if (this.s < 0) {
+            return this.t * this.DB;
+        }
+        return -1;
+    };
+    // BigInteger.prototype.bitCount = bnBitCount;
+    // (public) return number of set bits
+    BigInteger.prototype.bitCount = function () {
+        var r = 0;
+        var x = this.s & this.DM;
+        for (var i = 0; i < this.t; ++i) {
+            r += cbit(this[i] ^ x);
+        }
+        return r;
+    };
+    // BigInteger.prototype.testBit = bnTestBit;
+    // (public) true iff nth bit is set
+    BigInteger.prototype.testBit = function (n) {
+        var j = Math.floor(n / this.DB);
+        if (j >= this.t) {
+            return (this.s != 0);
+        }
+        return ((this[j] & (1 << (n % this.DB))) != 0);
+    };
+    // BigInteger.prototype.setBit = bnSetBit;
+    // (public) this | (1<<n)
+    BigInteger.prototype.setBit = function (n) {
+        return this.changeBit(n, op_or);
+    };
+    // BigInteger.prototype.clearBit = bnClearBit;
+    // (public) this & ~(1<<n)
+    BigInteger.prototype.clearBit = function (n) {
+        return this.changeBit(n, op_andnot);
+    };
+    // BigInteger.prototype.flipBit = bnFlipBit;
+    // (public) this ^ (1<<n)
+    BigInteger.prototype.flipBit = function (n) {
+        return this.changeBit(n, op_xor);
+    };
+    // BigInteger.prototype.add = bnAdd;
+    // (public) this + a
+    BigInteger.prototype.add = function (a) {
+        var r = nbi();
+        this.addTo(a, r);
+        return r;
+    };
+    // BigInteger.prototype.subtract = bnSubtract;
+    // (public) this - a
+    BigInteger.prototype.subtract = function (a) {
+        var r = nbi();
+        this.subTo(a, r);
+        return r;
+    };
+    // BigInteger.prototype.multiply = bnMultiply;
+    // (public) this * a
+    BigInteger.prototype.multiply = function (a) {
+        var r = nbi();
+        this.multiplyTo(a, r);
+        return r;
+    };
+    // BigInteger.prototype.divide = bnDivide;
+    // (public) this / a
+    BigInteger.prototype.divide = function (a) {
+        var r = nbi();
+        this.divRemTo(a, r, null);
+        return r;
+    };
+    // BigInteger.prototype.remainder = bnRemainder;
+    // (public) this % a
+    BigInteger.prototype.remainder = function (a) {
+        var r = nbi();
+        this.divRemTo(a, null, r);
+        return r;
+    };
+    // BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;
+    // (public) [this/a,this%a]
+    BigInteger.prototype.divideAndRemainder = function (a) {
+        var q = nbi();
+        var r = nbi();
+        this.divRemTo(a, q, r);
+        return [q, r];
+    };
+    // BigInteger.prototype.modPow = bnModPow;
+    // (public) this^e % m (HAC 14.85)
+    BigInteger.prototype.modPow = function (e, m) {
+        var i = e.bitLength();
+        var k;
+        var r = nbv(1);
+        var z;
+        if (i <= 0) {
+            return r;
+        }
+        else if (i < 18) {
+            k = 1;
+        }
+        else if (i < 48) {
+            k = 3;
+        }
+        else if (i < 144) {
+            k = 4;
+        }
+        else if (i < 768) {
+            k = 5;
+        }
+        else {
+            k = 6;
+        }
+        if (i < 8) {
+            z = new Classic(m);
+        }
+        else if (m.isEven()) {
+            z = new Barrett(m);
+        }
+        else {
+            z = new Montgomery(m);
+        }
+        // precomputation
+        var g = [];
+        var n = 3;
+        var k1 = k - 1;
+        var km = (1 << k) - 1;
+        g[1] = z.convert(this);
+        if (k > 1) {
+            var g2 = nbi();
+            z.sqrTo(g[1], g2);
+            while (n <= km) {
+                g[n] = nbi();
+                z.mulTo(g2, g[n - 2], g[n]);
+                n += 2;
+            }
+        }
+        var j = e.t - 1;
+        var w;
+        var is1 = true;
+        var r2 = nbi();
+        var t;
+        i = nbits(e[j]) - 1;
+        while (j >= 0) {
+            if (i >= k1) {
+                w = (e[j] >> (i - k1)) & km;
+            }
+            else {
+                w = (e[j] & ((1 << (i + 1)) - 1)) << (k1 - i);
+                if (j > 0) {
+                    w |= e[j - 1] >> (this.DB + i - k1);
+                }
+            }
+            n = k;
+            while ((w & 1) == 0) {
+                w >>= 1;
+                --n;
+            }
+            if ((i -= n) < 0) {
+                i += this.DB;
+                --j;
+            }
+            if (is1) { // ret == 1, don't bother squaring or multiplying it
+                g[w].copyTo(r);
+                is1 = false;
+            }
+            else {
+                while (n > 1) {
+                    z.sqrTo(r, r2);
+                    z.sqrTo(r2, r);
+                    n -= 2;
+                }
+                if (n > 0) {
+                    z.sqrTo(r, r2);
+                }
+                else {
+                    t = r;
+                    r = r2;
+                    r2 = t;
+                }
+                z.mulTo(r2, g[w], r);
+            }
+            while (j >= 0 && (e[j] & (1 << i)) == 0) {
+                z.sqrTo(r, r2);
+                t = r;
+                r = r2;
+                r2 = t;
+                if (--i < 0) {
+                    i = this.DB - 1;
+                    --j;
+                }
+            }
+        }
+        return z.revert(r);
+    };
+    // BigInteger.prototype.modInverse = bnModInverse;
+    // (public) 1/this % m (HAC 14.61)
+    BigInteger.prototype.modInverse = function (m) {
+        var ac = m.isEven();
+        if ((this.isEven() && ac) || m.signum() == 0) {
+            return BigInteger.ZERO;
+        }
+        var u = m.clone();
+        var v = this.clone();
+        var a = nbv(1);
+        var b = nbv(0);
+        var c = nbv(0);
+        var d = nbv(1);
+        while (u.signum() != 0) {
+            while (u.isEven()) {
+                u.rShiftTo(1, u);
+                if (ac) {
+                    if (!a.isEven() || !b.isEven()) {
+                        a.addTo(this, a);
+                        b.subTo(m, b);
+                    }
+                    a.rShiftTo(1, a);
+                }
+                else if (!b.isEven()) {
+                    b.subTo(m, b);
+                }
+                b.rShiftTo(1, b);
+            }
+            while (v.isEven()) {
+                v.rShiftTo(1, v);
+                if (ac) {
+                    if (!c.isEven() || !d.isEven()) {
+                        c.addTo(this, c);
+                        d.subTo(m, d);
+                    }
+                    c.rShiftTo(1, c);
+                }
+                else if (!d.isEven()) {
+                    d.subTo(m, d);
+                }
+                d.rShiftTo(1, d);
+            }
+            if (u.compareTo(v) >= 0) {
+                u.subTo(v, u);
+                if (ac) {
+                    a.subTo(c, a);
+                }
+                b.subTo(d, b);
+            }
+            else {
+                v.subTo(u, v);
+                if (ac) {
+                    c.subTo(a, c);
+                }
+                d.subTo(b, d);
+            }
+        }
+        if (v.compareTo(BigInteger.ONE) != 0) {
+            return BigInteger.ZERO;
+        }
+        if (d.compareTo(m) >= 0) {
+            return d.subtract(m);
+        }
+        if (d.signum() < 0) {
+            d.addTo(m, d);
+        }
+        else {
+            return d;
+        }
+        if (d.signum() < 0) {
+            return d.add(m);
+        }
+        else {
+            return d;
+        }
+    };
+    // BigInteger.prototype.pow = bnPow;
+    // (public) this^e
+    BigInteger.prototype.pow = function (e) {
+        return this.exp(e, new NullExp());
+    };
+    // BigInteger.prototype.gcd = bnGCD;
+    // (public) gcd(this,a) (HAC 14.54)
+    BigInteger.prototype.gcd = function (a) {
+        var x = (this.s < 0) ? this.negate() : this.clone();
+        var y = (a.s < 0) ? a.negate() : a.clone();
+        if (x.compareTo(y) < 0) {
+            var t = x;
+            x = y;
+            y = t;
+        }
+        var i = x.getLowestSetBit();
+        var g = y.getLowestSetBit();
+        if (g < 0) {
+            return x;
+        }
+        if (i < g) {
+            g = i;
+        }
+        if (g > 0) {
+            x.rShiftTo(g, x);
+            y.rShiftTo(g, y);
+        }
+        while (x.signum() > 0) {
+            if ((i = x.getLowestSetBit()) > 0) {
+                x.rShiftTo(i, x);
+            }
+            if ((i = y.getLowestSetBit()) > 0) {
+                y.rShiftTo(i, y);
+            }
+            if (x.compareTo(y) >= 0) {
+                x.subTo(y, x);
+                x.rShiftTo(1, x);
+            }
+            else {
+                y.subTo(x, y);
+                y.rShiftTo(1, y);
+            }
+        }
+        if (g > 0) {
+            y.lShiftTo(g, y);
+        }
+        return y;
+    };
+    // BigInteger.prototype.isProbablePrime = bnIsProbablePrime;
+    // (public) test primality with certainty >= 1-.5^t
+    BigInteger.prototype.isProbablePrime = function (t) {
+        var i;
+        var x = this.abs();
+        if (x.t == 1 && x[0] <= lowprimes[lowprimes.length - 1]) {
+            for (i = 0; i < lowprimes.length; ++i) {
+                if (x[0] == lowprimes[i]) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        if (x.isEven()) {
+            return false;
+        }
+        i = 1;
+        while (i < lowprimes.length) {
+            var m = lowprimes[i];
+            var j = i + 1;
+            while (j < lowprimes.length && m < lplim) {
+                m *= lowprimes[j++];
+            }
+            m = x.modInt(m);
+            while (i < j) {
+                if (m % lowprimes[i++] == 0) {
+                    return false;
+                }
+            }
+        }
+        return x.millerRabin(t);
+    };
+    //#endregion PUBLIC
+    //#region PROTECTED
+    // BigInteger.prototype.copyTo = bnpCopyTo;
+    // (protected) copy this to r
+    BigInteger.prototype.copyTo = function (r) {
+        for (var i = this.t - 1; i >= 0; --i) {
+            r[i] = this[i];
+        }
+        r.t = this.t;
+        r.s = this.s;
+    };
+    // BigInteger.prototype.fromInt = bnpFromInt;
+    // (protected) set from integer value x, -DV <= x < DV
+    BigInteger.prototype.fromInt = function (x) {
+        this.t = 1;
+        this.s = (x < 0) ? -1 : 0;
+        if (x > 0) {
+            this[0] = x;
+        }
+        else if (x < -1) {
+            this[0] = x + this.DV;
+        }
+        else {
+            this.t = 0;
+        }
+    };
+    // BigInteger.prototype.fromString = bnpFromString;
+    // (protected) set from string and radix
+    BigInteger.prototype.fromString = function (s, b) {
+        var k;
+        if (b == 16) {
+            k = 4;
+        }
+        else if (b == 8) {
+            k = 3;
+        }
+        else if (b == 256) {
+            k = 8;
+            /* byte array */
+        }
+        else if (b == 2) {
+            k = 1;
+        }
+        else if (b == 32) {
+            k = 5;
+        }
+        else if (b == 4) {
+            k = 2;
+        }
+        else {
+            this.fromRadix(s, b);
+            return;
+        }
+        this.t = 0;
+        this.s = 0;
+        var i = s.length;
+        var mi = false;
+        var sh = 0;
+        while (--i >= 0) {
+            var x = (k == 8) ? (+s[i]) & 0xff : intAt(s, i);
+            if (x < 0) {
+                if (s.charAt(i) == "-") {
+                    mi = true;
+                }
+                continue;
+            }
+            mi = false;
+            if (sh == 0) {
+                this[this.t++] = x;
+            }
+            else if (sh + k > this.DB) {
+                this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh;
+                this[this.t++] = (x >> (this.DB - sh));
+            }
+            else {
+                this[this.t - 1] |= x << sh;
+            }
+            sh += k;
+            if (sh >= this.DB) {
+                sh -= this.DB;
+            }
+        }
+        if (k == 8 && ((+s[0]) & 0x80) != 0) {
+            this.s = -1;
+            if (sh > 0) {
+                this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh;
+            }
+        }
+        this.clamp();
+        if (mi) {
+            BigInteger.ZERO.subTo(this, this);
+        }
+    };
+    // BigInteger.prototype.clamp = bnpClamp;
+    // (protected) clamp off excess high words
+    BigInteger.prototype.clamp = function () {
+        var c = this.s & this.DM;
+        while (this.t > 0 && this[this.t - 1] == c) {
+            --this.t;
+        }
+    };
+    // BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
+    // (protected) r = this << n*DB
+    BigInteger.prototype.dlShiftTo = function (n, r) {
+        var i;
+        for (i = this.t - 1; i >= 0; --i) {
+            r[i + n] = this[i];
+        }
+        for (i = n - 1; i >= 0; --i) {
+            r[i] = 0;
+        }
+        r.t = this.t + n;
+        r.s = this.s;
+    };
+    // BigInteger.prototype.drShiftTo = bnpDRShiftTo;
+    // (protected) r = this >> n*DB
+    BigInteger.prototype.drShiftTo = function (n, r) {
+        for (var i = n; i < this.t; ++i) {
+            r[i - n] = this[i];
+        }
+        r.t = Math.max(this.t - n, 0);
+        r.s = this.s;
+    };
+    // BigInteger.prototype.lShiftTo = bnpLShiftTo;
+    // (protected) r = this << n
+    BigInteger.prototype.lShiftTo = function (n, r) {
+        var bs = n % this.DB;
+        var cbs = this.DB - bs;
+        var bm = (1 << cbs) - 1;
+        var ds = Math.floor(n / this.DB);
+        var c = (this.s << bs) & this.DM;
+        for (var i = this.t - 1; i >= 0; --i) {
+            r[i + ds + 1] = (this[i] >> cbs) | c;
+            c = (this[i] & bm) << bs;
+        }
+        for (var i = ds - 1; i >= 0; --i) {
+            r[i] = 0;
+        }
+        r[ds] = c;
+        r.t = this.t + ds + 1;
+        r.s = this.s;
+        r.clamp();
+    };
+    // BigInteger.prototype.rShiftTo = bnpRShiftTo;
+    // (protected) r = this >> n
+    BigInteger.prototype.rShiftTo = function (n, r) {
+        r.s = this.s;
+        var ds = Math.floor(n / this.DB);
+        if (ds >= this.t) {
+            r.t = 0;
+            return;
+        }
+        var bs = n % this.DB;
+        var cbs = this.DB - bs;
+        var bm = (1 << bs) - 1;
+        r[0] = this[ds] >> bs;
+        for (var i = ds + 1; i < this.t; ++i) {
+            r[i - ds - 1] |= (this[i] & bm) << cbs;
+            r[i - ds] = this[i] >> bs;
+        }
+        if (bs > 0) {
+            r[this.t - ds - 1] |= (this.s & bm) << cbs;
+        }
+        r.t = this.t - ds;
+        r.clamp();
+    };
+    // BigInteger.prototype.subTo = bnpSubTo;
+    // (protected) r = this - a
+    BigInteger.prototype.subTo = function (a, r) {
+        var i = 0;
+        var c = 0;
+        var m = Math.min(a.t, this.t);
+        while (i < m) {
+            c += this[i] - a[i];
+            r[i++] = c & this.DM;
+            c >>= this.DB;
+        }
+        if (a.t < this.t) {
+            c -= a.s;
+            while (i < this.t) {
+                c += this[i];
+                r[i++] = c & this.DM;
+                c >>= this.DB;
+            }
+            c += this.s;
+        }
+        else {
+            c += this.s;
+            while (i < a.t) {
+                c -= a[i];
+                r[i++] = c & this.DM;
+                c >>= this.DB;
+            }
+            c -= a.s;
+        }
+        r.s = (c < 0) ? -1 : 0;
+        if (c < -1) {
+            r[i++] = this.DV + c;
+        }
+        else if (c > 0) {
+            r[i++] = c;
+        }
+        r.t = i;
+        r.clamp();
+    };
+    // BigInteger.prototype.multiplyTo = bnpMultiplyTo;
+    // (protected) r = this * a, r != this,a (HAC 14.12)
+    // "this" should be the larger one if appropriate.
+    BigInteger.prototype.multiplyTo = function (a, r) {
+        var x = this.abs();
+        var y = a.abs();
+        var i = x.t;
+        r.t = i + y.t;
+        while (--i >= 0) {
+            r[i] = 0;
+        }
+        for (i = 0; i < y.t; ++i) {
+            r[i + x.t] = x.am(0, y[i], r, i, 0, x.t);
+        }
+        r.s = 0;
+        r.clamp();
+        if (this.s != a.s) {
+            BigInteger.ZERO.subTo(r, r);
+        }
+    };
+    // BigInteger.prototype.squareTo = bnpSquareTo;
+    // (protected) r = this^2, r != this (HAC 14.16)
+    BigInteger.prototype.squareTo = function (r) {
+        var x = this.abs();
+        var i = r.t = 2 * x.t;
+        while (--i >= 0) {
+            r[i] = 0;
+        }
+        for (i = 0; i < x.t - 1; ++i) {
+            var c = x.am(i, x[i], r, 2 * i, 0, 1);
+            if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) {
+                r[i + x.t] -= x.DV;
+                r[i + x.t + 1] = 1;
+            }
+        }
+        if (r.t > 0) {
+            r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1);
+        }
+        r.s = 0;
+        r.clamp();
+    };
+    // BigInteger.prototype.divRemTo = bnpDivRemTo;
+    // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
+    // r != q, this != m.  q or r may be null.
+    BigInteger.prototype.divRemTo = function (m, q, r) {
+        var pm = m.abs();
+        if (pm.t <= 0) {
+            return;
+        }
+        var pt = this.abs();
+        if (pt.t < pm.t) {
+            if (q != null) {
+                q.fromInt(0);
+            }
+            if (r != null) {
+                this.copyTo(r);
+            }
+            return;
+        }
+        if (r == null) {
+            r = nbi();
+        }
+        var y = nbi();
+        var ts = this.s;
+        var ms = m.s;
+        var nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus
+        if (nsh > 0) {
+            pm.lShiftTo(nsh, y);
+            pt.lShiftTo(nsh, r);
+        }
+        else {
+            pm.copyTo(y);
+            pt.copyTo(r);
+        }
+        var ys = y.t;
+        var y0 = y[ys - 1];
+        if (y0 == 0) {
+            return;
+        }
+        var yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0);
+        var d1 = this.FV / yt;
+        var d2 = (1 << this.F1) / yt;
+        var e = 1 << this.F2;
+        var i = r.t;
+        var j = i - ys;
+        var t = (q == null) ? nbi() : q;
+        y.dlShiftTo(j, t);
+        if (r.compareTo(t) >= 0) {
+            r[r.t++] = 1;
+            r.subTo(t, r);
+        }
+        BigInteger.ONE.dlShiftTo(ys, t);
+        t.subTo(y, y); // "negative" y so we can replace sub with am later
+        while (y.t < ys) {
+            y[y.t++] = 0;
+        }
+        while (--j >= 0) {
+            // Estimate quotient digit
+            var qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2);
+            if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out
+                y.dlShiftTo(j, t);
+                r.subTo(t, r);
+                while (r[i] < --qd) {
+                    r.subTo(t, r);
+                }
+            }
+        }
+        if (q != null) {
+            r.drShiftTo(ys, q);
+            if (ts != ms) {
+                BigInteger.ZERO.subTo(q, q);
+            }
+        }
+        r.t = ys;
+        r.clamp();
+        if (nsh > 0) {
+            r.rShiftTo(nsh, r);
+        } // Denormalize remainder
+        if (ts < 0) {
+            BigInteger.ZERO.subTo(r, r);
+        }
+    };
+    // BigInteger.prototype.invDigit = bnpInvDigit;
+    // (protected) return "-1/this % 2^DB"; useful for Mont. reduction
+    // justification:
+    //         xy == 1 (mod m)
+    //         xy =  1+km
+    //   xy(2-xy) = (1+km)(1-km)
+    // x[y(2-xy)] = 1-k^2m^2
+    // x[y(2-xy)] == 1 (mod m^2)
+    // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
+    // should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
+    // JS multiply "overflows" differently from C/C++, so care is needed here.
+    BigInteger.prototype.invDigit = function () {
+        if (this.t < 1) {
+            return 0;
+        }
+        var x = this[0];
+        if ((x & 1) == 0) {
+            return 0;
+        }
+        var y = x & 3; // y == 1/x mod 2^2
+        y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4
+        y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8
+        y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16
+        // last step - calculate inverse mod DV directly;
+        // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
+        y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^dbits
+        // we really want the negative inverse, and -DV < y < DV
+        return (y > 0) ? this.DV - y : -y;
+    };
+    // BigInteger.prototype.isEven = bnpIsEven;
+    // (protected) true iff this is even
+    BigInteger.prototype.isEven = function () {
+        return ((this.t > 0) ? (this[0] & 1) : this.s) == 0;
+    };
+    // BigInteger.prototype.exp = bnpExp;
+    // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
+    BigInteger.prototype.exp = function (e, z) {
+        if (e > 0xffffffff || e < 1) {
+            return BigInteger.ONE;
+        }
+        var r = nbi();
+        var r2 = nbi();
+        var g = z.convert(this);
+        var i = nbits(e) - 1;
+        g.copyTo(r);
+        while (--i >= 0) {
+            z.sqrTo(r, r2);
+            if ((e & (1 << i)) > 0) {
+                z.mulTo(r2, g, r);
+            }
+            else {
+                var t = r;
+                r = r2;
+                r2 = t;
+            }
+        }
+        return z.revert(r);
+    };
+    // BigInteger.prototype.chunkSize = bnpChunkSize;
+    // (protected) return x s.t. r^x < DV
+    BigInteger.prototype.chunkSize = function (r) {
+        return Math.floor(Math.LN2 * this.DB / Math.log(r));
+    };
+    // BigInteger.prototype.toRadix = bnpToRadix;
+    // (protected) convert to radix string
+    BigInteger.prototype.toRadix = function (b) {
+        if (b == null) {
+            b = 10;
+        }
+        if (this.signum() == 0 || b < 2 || b > 36) {
+            return "0";
+        }
+        var cs = this.chunkSize(b);
+        var a = Math.pow(b, cs);
+        var d = nbv(a);
+        var y = nbi();
+        var z = nbi();
+        var r = "";
+        this.divRemTo(d, y, z);
+        while (y.signum() > 0) {
+            r = (a + z.intValue()).toString(b).substr(1) + r;
+            y.divRemTo(d, y, z);
+        }
+        return z.intValue().toString(b) + r;
+    };
+    // BigInteger.prototype.fromRadix = bnpFromRadix;
+    // (protected) convert from radix string
+    BigInteger.prototype.fromRadix = function (s, b) {
+        this.fromInt(0);
+        if (b == null) {
+            b = 10;
+        }
+        var cs = this.chunkSize(b);
+        var d = Math.pow(b, cs);
+        var mi = false;
+        var j = 0;
+        var w = 0;
+        for (var i = 0; i < s.length; ++i) {
+            var x = intAt(s, i);
+            if (x < 0) {
+                if (s.charAt(i) == "-" && this.signum() == 0) {
+                    mi = true;
+                }
+                continue;
+            }
+            w = b * w + x;
+            if (++j >= cs) {
+                this.dMultiply(d);
+                this.dAddOffset(w, 0);
+                j = 0;
+                w = 0;
+            }
+        }
+        if (j > 0) {
+            this.dMultiply(Math.pow(b, j));
+            this.dAddOffset(w, 0);
+        }
+        if (mi) {
+            BigInteger.ZERO.subTo(this, this);
+        }
+    };
+    // BigInteger.prototype.fromNumber = bnpFromNumber;
+    // (protected) alternate constructor
+    BigInteger.prototype.fromNumber = function (a, b, c) {
+        if ("number" == typeof b) {
+            // new BigInteger(int,int,RNG)
+            if (a < 2) {
+                this.fromInt(1);
+            }
+            else {
+                this.fromNumber(a, c);
+                if (!this.testBit(a - 1)) {
+                    // force MSB set
+                    this.bitwiseTo(BigInteger.ONE.shiftLeft(a - 1), op_or, this);
+                }
+                if (this.isEven()) {
+                    this.dAddOffset(1, 0);
+                } // force odd
+                while (!this.isProbablePrime(b)) {
+                    this.dAddOffset(2, 0);
+                    if (this.bitLength() > a) {
+                        this.subTo(BigInteger.ONE.shiftLeft(a - 1), this);
+                    }
+                }
+            }
+        }
+        else {
+            // new BigInteger(int,RNG)
+            var x = [];
+            var t = a & 7;
+            x.length = (a >> 3) + 1;
+            b.nextBytes(x);
+            if (t > 0) {
+                x[0] &= ((1 << t) - 1);
+            }
+            else {
+                x[0] = 0;
+            }
+            this.fromString(x, 256);
+        }
+    };
+    // BigInteger.prototype.bitwiseTo = bnpBitwiseTo;
+    // (protected) r = this op a (bitwise)
+    BigInteger.prototype.bitwiseTo = function (a, op, r) {
+        var i;
+        var f;
+        var m = Math.min(a.t, this.t);
+        for (i = 0; i < m; ++i) {
+            r[i] = op(this[i], a[i]);
+        }
+        if (a.t < this.t) {
+            f = a.s & this.DM;
+            for (i = m; i < this.t; ++i) {
+                r[i] = op(this[i], f);
+            }
+            r.t = this.t;
+        }
+        else {
+            f = this.s & this.DM;
+            for (i = m; i < a.t; ++i) {
+                r[i] = op(f, a[i]);
+            }
+            r.t = a.t;
+        }
+        r.s = op(this.s, a.s);
+        r.clamp();
+    };
+    // BigInteger.prototype.changeBit = bnpChangeBit;
+    // (protected) this op (1<<n)
+    BigInteger.prototype.changeBit = function (n, op) {
+        var r = BigInteger.ONE.shiftLeft(n);
+        this.bitwiseTo(r, op, r);
+        return r;
+    };
+    // BigInteger.prototype.addTo = bnpAddTo;
+    // (protected) r = this + a
+    BigInteger.prototype.addTo = function (a, r) {
+        var i = 0;
+        var c = 0;
+        var m = Math.min(a.t, this.t);
+        while (i < m) {
+            c += this[i] + a[i];
+            r[i++] = c & this.DM;
+            c >>= this.DB;
+        }
+        if (a.t < this.t) {
+            c += a.s;
+            while (i < this.t) {
+                c += this[i];
+                r[i++] = c & this.DM;
+                c >>= this.DB;
+            }
+            c += this.s;
+        }
+        else {
+            c += this.s;
+            while (i < a.t) {
+                c += a[i];
+                r[i++] = c & this.DM;
+                c >>= this.DB;
+            }
+            c += a.s;
+        }
+        r.s = (c < 0) ? -1 : 0;
+        if (c > 0) {
+            r[i++] = c;
+        }
+        else if (c < -1) {
+            r[i++] = this.DV + c;
+        }
+        r.t = i;
+        r.clamp();
+    };
+    // BigInteger.prototype.dMultiply = bnpDMultiply;
+    // (protected) this *= n, this >= 0, 1 < n < DV
+    BigInteger.prototype.dMultiply = function (n) {
+        this[this.t] = this.am(0, n - 1, this, 0, 0, this.t);
+        ++this.t;
+        this.clamp();
+    };
+    // BigInteger.prototype.dAddOffset = bnpDAddOffset;
+    // (protected) this += n << w words, this >= 0
+    BigInteger.prototype.dAddOffset = function (n, w) {
+        if (n == 0) {
+            return;
+        }
+        while (this.t <= w) {
+            this[this.t++] = 0;
+        }
+        this[w] += n;
+        while (this[w] >= this.DV) {
+            this[w] -= this.DV;
+            if (++w >= this.t) {
+                this[this.t++] = 0;
+            }
+            ++this[w];
+        }
+    };
+    // BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;
+    // (protected) r = lower n words of "this * a", a.t <= n
+    // "this" should be the larger one if appropriate.
+    BigInteger.prototype.multiplyLowerTo = function (a, n, r) {
+        var i = Math.min(this.t + a.t, n);
+        r.s = 0; // assumes a,this >= 0
+        r.t = i;
+        while (i > 0) {
+            r[--i] = 0;
+        }
+        for (var j = r.t - this.t; i < j; ++i) {
+            r[i + this.t] = this.am(0, a[i], r, i, 0, this.t);
+        }
+        for (var j = Math.min(a.t, n); i < j; ++i) {
+            this.am(0, a[i], r, i, 0, n - i);
+        }
+        r.clamp();
+    };
+    // BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;
+    // (protected) r = "this * a" without lower n words, n > 0
+    // "this" should be the larger one if appropriate.
+    BigInteger.prototype.multiplyUpperTo = function (a, n, r) {
+        --n;
+        var i = r.t = this.t + a.t - n;
+        r.s = 0; // assumes a,this >= 0
+        while (--i >= 0) {
+            r[i] = 0;
+        }
+        for (i = Math.max(n - this.t, 0); i < a.t; ++i) {
+            r[this.t + i - n] = this.am(n - i, a[i], r, 0, 0, this.t + i - n);
+        }
+        r.clamp();
+        r.drShiftTo(1, r);
+    };
+    // BigInteger.prototype.modInt = bnpModInt;
+    // (protected) this % n, n < 2^26
+    BigInteger.prototype.modInt = function (n) {
+        if (n <= 0) {
+            return 0;
+        }
+        var d = this.DV % n;
+        var r = (this.s < 0) ? n - 1 : 0;
+        if (this.t > 0) {
+            if (d == 0) {
+                r = this[0] % n;
+            }
+            else {
+                for (var i = this.t - 1; i >= 0; --i) {
+                    r = (d * r + this[i]) % n;
+                }
+            }
+        }
+        return r;
+    };
+    // BigInteger.prototype.millerRabin = bnpMillerRabin;
+    // (protected) true if probably prime (HAC 4.24, Miller-Rabin)
+    BigInteger.prototype.millerRabin = function (t) {
+        var n1 = this.subtract(BigInteger.ONE);
+        var k = n1.getLowestSetBit();
+        if (k <= 0) {
+            return false;
+        }
+        var r = n1.shiftRight(k);
+        t = (t + 1) >> 1;
+        if (t > lowprimes.length) {
+            t = lowprimes.length;
+        }
+        var a = nbi();
+        for (var i = 0; i < t; ++i) {
+            // Pick bases at random, instead of starting at 2
+            a.fromInt(lowprimes[Math.floor(Math.random() * lowprimes.length)]);
+            var y = a.modPow(r, this);
+            if (y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
+                var j = 1;
+                while (j++ < k && y.compareTo(n1) != 0) {
+                    y = y.modPowInt(2, this);
+                    if (y.compareTo(BigInteger.ONE) == 0) {
+                        return false;
+                    }
+                }
+                if (y.compareTo(n1) != 0) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    };
+    // BigInteger.prototype.square = bnSquare;
+    // (public) this^2
+    BigInteger.prototype.square = function () {
+        var r = nbi();
+        this.squareTo(r);
+        return r;
+    };
+    //#region ASYNC
+    // Public API method
+    BigInteger.prototype.gcda = function (a, callback) {
+        var x = (this.s < 0) ? this.negate() : this.clone();
+        var y = (a.s < 0) ? a.negate() : a.clone();
+        if (x.compareTo(y) < 0) {
+            var t = x;
+            x = y;
+            y = t;
+        }
+        var i = x.getLowestSetBit();
+        var g = y.getLowestSetBit();
+        if (g < 0) {
+            callback(x);
+            return;
+        }
+        if (i < g) {
+            g = i;
+        }
+        if (g > 0) {
+            x.rShiftTo(g, x);
+            y.rShiftTo(g, y);
+        }
+        // Workhorse of the algorithm, gets called 200 - 800 times per 512 bit keygen.
+        var gcda1 = function () {
+            if ((i = x.getLowestSetBit()) > 0) {
+                x.rShiftTo(i, x);
+            }
+            if ((i = y.getLowestSetBit()) > 0) {
+                y.rShiftTo(i, y);
+            }
+            if (x.compareTo(y) >= 0) {
+                x.subTo(y, x);
+                x.rShiftTo(1, x);
+            }
+            else {
+                y.subTo(x, y);
+                y.rShiftTo(1, y);
+            }
+            if (!(x.signum() > 0)) {
+                if (g > 0) {
+                    y.lShiftTo(g, y);
+                }
+                setTimeout(function () { callback(y); }, 0); // escape
+            }
+            else {
+                setTimeout(gcda1, 0);
+            }
+        };
+        setTimeout(gcda1, 10);
+    };
+    // (protected) alternate constructor
+    BigInteger.prototype.fromNumberAsync = function (a, b, c, callback) {
+        if ("number" == typeof b) {
+            if (a < 2) {
+                this.fromInt(1);
+            }
+            else {
+                this.fromNumber(a, c);
+                if (!this.testBit(a - 1)) {
+                    this.bitwiseTo(BigInteger.ONE.shiftLeft(a - 1), op_or, this);
+                }
+                if (this.isEven()) {
+                    this.dAddOffset(1, 0);
+                }
+                var bnp_1 = this;
+                var bnpfn1_1 = function () {
+                    bnp_1.dAddOffset(2, 0);
+                    if (bnp_1.bitLength() > a) {
+                        bnp_1.subTo(BigInteger.ONE.shiftLeft(a - 1), bnp_1);
+                    }
+                    if (bnp_1.isProbablePrime(b)) {
+                        setTimeout(function () { callback(); }, 0); // escape
+                    }
+                    else {
+                        setTimeout(bnpfn1_1, 0);
+                    }
+                };
+                setTimeout(bnpfn1_1, 0);
+            }
+        }
+        else {
+            var x = [];
+            var t = a & 7;
+            x.length = (a >> 3) + 1;
+            b.nextBytes(x);
+            if (t > 0) {
+                x[0] &= ((1 << t) - 1);
+            }
+            else {
+                x[0] = 0;
+            }
+            this.fromString(x, 256);
+        }
+    };
+    return BigInteger;
+}());
+//#region REDUCERS
+//#region NullExp
+var NullExp = /** @class */ (function () {
+    function NullExp() {
+    }
+    // NullExp.prototype.convert = nNop;
+    NullExp.prototype.convert = function (x) {
+        return x;
+    };
+    // NullExp.prototype.revert = nNop;
+    NullExp.prototype.revert = function (x) {
+        return x;
+    };
+    // NullExp.prototype.mulTo = nMulTo;
+    NullExp.prototype.mulTo = function (x, y, r) {
+        x.multiplyTo(y, r);
+    };
+    // NullExp.prototype.sqrTo = nSqrTo;
+    NullExp.prototype.sqrTo = function (x, r) {
+        x.squareTo(r);
+    };
+    return NullExp;
+}());
+// Modular reduction using "classic" algorithm
+var Classic = /** @class */ (function () {
+    function Classic(m) {
+        this.m = m;
+    }
+    // Classic.prototype.convert = cConvert;
+    Classic.prototype.convert = function (x) {
+        if (x.s < 0 || x.compareTo(this.m) >= 0) {
+            return x.mod(this.m);
+        }
+        else {
+            return x;
+        }
+    };
+    // Classic.prototype.revert = cRevert;
+    Classic.prototype.revert = function (x) {
+        return x;
+    };
+    // Classic.prototype.reduce = cReduce;
+    Classic.prototype.reduce = function (x) {
+        x.divRemTo(this.m, null, x);
+    };
+    // Classic.prototype.mulTo = cMulTo;
+    Classic.prototype.mulTo = function (x, y, r) {
+        x.multiplyTo(y, r);
+        this.reduce(r);
+    };
+    // Classic.prototype.sqrTo = cSqrTo;
+    Classic.prototype.sqrTo = function (x, r) {
+        x.squareTo(r);
+        this.reduce(r);
+    };
+    return Classic;
+}());
+//#endregion
+//#region Montgomery
+// Montgomery reduction
+var Montgomery = /** @class */ (function () {
+    function Montgomery(m) {
+        this.m = m;
+        this.mp = m.invDigit();
+        this.mpl = this.mp & 0x7fff;
+        this.mph = this.mp >> 15;
+        this.um = (1 << (m.DB - 15)) - 1;
+        this.mt2 = 2 * m.t;
+    }
+    // Montgomery.prototype.convert = montConvert;
+    // xR mod m
+    Montgomery.prototype.convert = function (x) {
+        var r = nbi();
+        x.abs().dlShiftTo(this.m.t, r);
+        r.divRemTo(this.m, null, r);
+        if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) {
+            this.m.subTo(r, r);
+        }
+        return r;
+    };
+    // Montgomery.prototype.revert = montRevert;
+    // x/R mod m
+    Montgomery.prototype.revert = function (x) {
+        var r = nbi();
+        x.copyTo(r);
+        this.reduce(r);
+        return r;
+    };
+    // Montgomery.prototype.reduce = montReduce;
+    // x = x/R mod m (HAC 14.32)
+    Montgomery.prototype.reduce = function (x) {
+        while (x.t <= this.mt2) {
+            // pad x so am has enough room later
+            x[x.t++] = 0;
+        }
+        for (var i = 0; i < this.m.t; ++i) {
+            // faster way of calculating u0 = x[i]*mp mod DV
+            var j = x[i] & 0x7fff;
+            var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM;
+            // use am to combine the multiply-shift-add into one call
+            j = i + this.m.t;
+            x[j] += this.m.am(0, u0, x, i, 0, this.m.t);
+            // propagate carry
+            while (x[j] >= x.DV) {
+                x[j] -= x.DV;
+                x[++j]++;
+            }
+        }
+        x.clamp();
+        x.drShiftTo(this.m.t, x);
+        if (x.compareTo(this.m) >= 0) {
+            x.subTo(this.m, x);
+        }
+    };
+    // Montgomery.prototype.mulTo = montMulTo;
+    // r = "xy/R mod m"; x,y != r
+    Montgomery.prototype.mulTo = function (x, y, r) {
+        x.multiplyTo(y, r);
+        this.reduce(r);
+    };
+    // Montgomery.prototype.sqrTo = montSqrTo;
+    // r = "x^2/R mod m"; x != r
+    Montgomery.prototype.sqrTo = function (x, r) {
+        x.squareTo(r);
+        this.reduce(r);
+    };
+    return Montgomery;
+}());
+//#endregion Montgomery
+//#region Barrett
+// Barrett modular reduction
+var Barrett = /** @class */ (function () {
+    function Barrett(m) {
+        this.m = m;
+        // setup Barrett
+        this.r2 = nbi();
+        this.q3 = nbi();
+        BigInteger.ONE.dlShiftTo(2 * m.t, this.r2);
+        this.mu = this.r2.divide(m);
+    }
+    // Barrett.prototype.convert = barrettConvert;
+    Barrett.prototype.convert = function (x) {
+        if (x.s < 0 || x.t > 2 * this.m.t) {
+            return x.mod(this.m);
+        }
+        else if (x.compareTo(this.m) < 0) {
+            return x;
+        }
+        else {
+            var r = nbi();
+            x.copyTo(r);
+            this.reduce(r);
+            return r;
+        }
+    };
+    // Barrett.prototype.revert = barrettRevert;
+    Barrett.prototype.revert = function (x) {
+        return x;
+    };
+    // Barrett.prototype.reduce = barrettReduce;
+    // x = x mod m (HAC 14.42)
+    Barrett.prototype.reduce = function (x) {
+        x.drShiftTo(this.m.t - 1, this.r2);
+        if (x.t > this.m.t + 1) {
+            x.t = this.m.t + 1;
+            x.clamp();
+        }
+        this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3);
+        this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2);
+        while (x.compareTo(this.r2) < 0) {
+            x.dAddOffset(1, this.m.t + 1);
+        }
+        x.subTo(this.r2, x);
+        while (x.compareTo(this.m) >= 0) {
+            x.subTo(this.m, x);
+        }
+    };
+    // Barrett.prototype.mulTo = barrettMulTo;
+    // r = x*y mod m; x,y != r
+    Barrett.prototype.mulTo = function (x, y, r) {
+        x.multiplyTo(y, r);
+        this.reduce(r);
+    };
+    // Barrett.prototype.sqrTo = barrettSqrTo;
+    // r = x^2 mod m; x != r
+    Barrett.prototype.sqrTo = function (x, r) {
+        x.squareTo(r);
+        this.reduce(r);
+    };
+    return Barrett;
+}());
+//#endregion
+//#endregion REDUCERS
+// return new, unset BigInteger
+function nbi() { return new BigInteger(null); }
+function parseBigInt(str, r) {
+    return new BigInteger(str, r);
+}
+// am: Compute w_j += (x*this_i), propagate carries,
+// c is initial carry, returns final carry.
+// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
+// We need to select the fastest one that works in this environment.
+// am1: use a single mult and divide to get the high bits,
+// max digit bits should be 26 because
+// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
+function am1(i, x, w, j, c, n) {
+    while (--n >= 0) {
+        var v = x * this[i++] + w[j] + c;
+        c = Math.floor(v / 0x4000000);
+        w[j++] = v & 0x3ffffff;
+    }
+    return c;
+}
+// am2 avoids a big mult-and-extract completely.
+// Max digit bits should be <= 30 because we do bitwise ops
+// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
+function am2(i, x, w, j, c, n) {
+    var xl = x & 0x7fff;
+    var xh = x >> 15;
+    while (--n >= 0) {
+        var l = this[i] & 0x7fff;
+        var h = this[i++] >> 15;
+        var m = xh * l + h * xl;
+        l = xl * l + ((m & 0x7fff) << 15) + w[j] + (c & 0x3fffffff);
+        c = (l >>> 30) + (m >>> 15) + xh * h + (c >>> 30);
+        w[j++] = l & 0x3fffffff;
+    }
+    return c;
+}
+// Alternately, set max digit bits to 28 since some
+// browsers slow down when dealing with 32-bit numbers.
+function am3(i, x, w, j, c, n) {
+    var xl = x & 0x3fff;
+    var xh = x >> 14;
+    while (--n >= 0) {
+        var l = this[i] & 0x3fff;
+        var h = this[i++] >> 14;
+        var m = xh * l + h * xl;
+        l = xl * l + ((m & 0x3fff) << 14) + w[j] + c;
+        c = (l >> 28) + (m >> 14) + xh * h;
+        w[j++] = l & 0xfffffff;
+    }
+    return c;
+}
+if (j_lm && (navigator2.appName == "Microsoft Internet Explorer")) {
+    BigInteger.prototype.am = am2;
+    dbits = 30;
+}
+else if (j_lm && (navigator2.appName != "Netscape")) {
+    BigInteger.prototype.am = am1;
+    dbits = 26;
+}
+else { // Mozilla/Netscape seems to prefer am3
+    BigInteger.prototype.am = am3;
+    dbits = 28;
+}
+BigInteger.prototype.DB = dbits;
+BigInteger.prototype.DM = ((1 << dbits) - 1);
+BigInteger.prototype.DV = (1 << dbits);
+var BI_FP = 52;
+BigInteger.prototype.FV = Math.pow(2, BI_FP);
+BigInteger.prototype.F1 = BI_FP - dbits;
+BigInteger.prototype.F2 = 2 * dbits - BI_FP;
+// Digit conversions
+var BI_RC = [];
+var rr;
+var vv;
+rr = "0".charCodeAt(0);
+for (vv = 0; vv <= 9; ++vv) {
+    BI_RC[rr++] = vv;
+}
+rr = "a".charCodeAt(0);
+for (vv = 10; vv < 36; ++vv) {
+    BI_RC[rr++] = vv;
+}
+rr = "A".charCodeAt(0);
+for (vv = 10; vv < 36; ++vv) {
+    BI_RC[rr++] = vv;
+}
+function intAt(s, i) {
+    var c = BI_RC[s.charCodeAt(i)];
+    return (c == null) ? -1 : c;
+}
+// return bigint initialized to value
+function nbv(i) {
+    var r = nbi();
+    r.fromInt(i);
+    return r;
+}
+// returns bit length of the integer x
+function nbits(x) {
+    var r = 1;
+    var t;
+    if ((t = x >>> 16) != 0) {
+        x = t;
+        r += 16;
+    }
+    if ((t = x >> 8) != 0) {
+        x = t;
+        r += 8;
+    }
+    if ((t = x >> 4) != 0) {
+        x = t;
+        r += 4;
+    }
+    if ((t = x >> 2) != 0) {
+        x = t;
+        r += 2;
+    }
+    if ((t = x >> 1) != 0) {
+        x = t;
+        r += 1;
+    }
+    return r;
+}
+// "constants"
+BigInteger.ZERO = nbv(0);
+BigInteger.ONE = nbv(1);
+
+// prng4.js - uses Arcfour as a PRNG
+var Arcfour = /** @class */ (function () {
+    function Arcfour() {
+        this.i = 0;
+        this.j = 0;
+        this.S = [];
+    }
+    // Arcfour.prototype.init = ARC4init;
+    // Initialize arcfour context from key, an array of ints, each from [0..255]
+    Arcfour.prototype.init = function (key) {
+        var i;
+        var j;
+        var t;
+        for (i = 0; i < 256; ++i) {
+            this.S[i] = i;
+        }
+        j = 0;
+        for (i = 0; i < 256; ++i) {
+            j = (j + this.S[i] + key[i % key.length]) & 255;
+            t = this.S[i];
+            this.S[i] = this.S[j];
+            this.S[j] = t;
+        }
+        this.i = 0;
+        this.j = 0;
+    };
+    // Arcfour.prototype.next = ARC4next;
+    Arcfour.prototype.next = function () {
+        var t;
+        this.i = (this.i + 1) & 255;
+        this.j = (this.j + this.S[this.i]) & 255;
+        t = this.S[this.i];
+        this.S[this.i] = this.S[this.j];
+        this.S[this.j] = t;
+        return this.S[(t + this.S[this.i]) & 255];
+    };
+    return Arcfour;
+}());
+// Plug in your RNG constructor here
+function prng_newstate() {
+    return new Arcfour();
+}
+// Pool size must be a multiple of 4 and greater than 32.
+// An array of bytes the size of the pool will be passed to init()
+var rng_psize = 256;
+
+// Random number generator - requires a PRNG backend, e.g. prng4.js
+var rng_state;
+var rng_pool = null;
+var rng_pptr;
+// Initialize the pool with junk if needed.
+if (rng_pool == null) {
+    rng_pool = [];
+    rng_pptr = 0;
+    var t = void 0;
+    if (window2.crypto && window2.crypto.getRandomValues) {
+        // Extract entropy (2048 bits) from RNG if available
+        var z = new Uint32Array(256);
+        window2.crypto.getRandomValues(z);
+        for (t = 0; t < z.length; ++t) {
+            rng_pool[rng_pptr++] = z[t] & 255;
+        }
+    }
+    // Use mouse events for entropy, if we do not have enough entropy by the time
+    // we need it, entropy will be generated by Math.random.
+    var onMouseMoveListener_1 = function (ev) {
+        this.count = this.count || 0;
+        if (this.count >= 256 || rng_pptr >= rng_psize) {
+            if (window2.removeEventListener) {
+                window2.removeEventListener("mousemove", onMouseMoveListener_1, false);
+            }
+            else if (window2.detachEvent) {
+                window2.detachEvent("onmousemove", onMouseMoveListener_1);
+            }
+            return;
+        }
+        try {
+            var mouseCoordinates = ev.x + ev.y;
+            rng_pool[rng_pptr++] = mouseCoordinates & 255;
+            this.count += 1;
+        }
+        catch (e) {
+            // Sometimes Firefox will deny permission to access event properties for some reason. Ignore.
+        }
+    };
+    if (window2.addEventListener) {
+        window2.addEventListener("mousemove", onMouseMoveListener_1, false);
+    }
+    else if (window2.attachEvent) {
+        window2.attachEvent("onmousemove", onMouseMoveListener_1);
+    }
+}
+function rng_get_byte() {
+    if (rng_state == null) {
+        rng_state = prng_newstate();
+        // At this point, we may not have collected enough entropy.  If not, fall back to Math.random
+        while (rng_pptr < rng_psize) {
+            var random = Math.floor(65536 * Math.random());
+            rng_pool[rng_pptr++] = random & 255;
+        }
+        rng_state.init(rng_pool);
+        for (rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) {
+            rng_pool[rng_pptr] = 0;
+        }
+        rng_pptr = 0;
+    }
+    // TODO: allow reseeding after first request
+    return rng_state.next();
+}
+var SecureRandom = /** @class */ (function () {
+    function SecureRandom() {
+    }
+    SecureRandom.prototype.nextBytes = function (ba) {
+        for (var i = 0; i < ba.length; ++i) {
+            ba[i] = rng_get_byte();
+        }
+    };
+    return SecureRandom;
+}());
+
+// Depends on jsbn.js and rng.js
+// function linebrk(s,n) {
+//   var ret = "";
+//   var i = 0;
+//   while(i + n < s.length) {
+//     ret += s.substring(i,i+n) + "\n";
+//     i += n;
+//   }
+//   return ret + s.substring(i,s.length);
+// }
+// function byte2Hex(b) {
+//   if(b < 0x10)
+//     return "0" + b.toString(16);
+//   else
+//     return b.toString(16);
+// }
+function pkcs1pad1(s, n) {
+    if (n < s.length + 22) {
+        console.error("Message too long for RSA");
+        return null;
+    }
+    var len = n - s.length - 6;
+    var filler = "";
+    for (var f = 0; f < len; f += 2) {
+        filler += "ff";
+    }
+    var m = "0001" + filler + "00" + s;
+    return parseBigInt(m, 16);
+}
+// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
+function pkcs1pad2(s, n) {
+    if (n < s.length + 11) { // TODO: fix for utf-8
+        console.error("Message too long for RSA");
+        return null;
+    }
+    var ba = [];
+    var i = s.length - 1;
+    while (i >= 0 && n > 0) {
+        var c = s.charCodeAt(i--);
+        if (c < 128) { // encode using utf-8
+            ba[--n] = c;
+        }
+        else if ((c > 127) && (c < 2048)) {
+            ba[--n] = (c & 63) | 128;
+            ba[--n] = (c >> 6) | 192;
+        }
+        else {
+            ba[--n] = (c & 63) | 128;
+            ba[--n] = ((c >> 6) & 63) | 128;
+            ba[--n] = (c >> 12) | 224;
+        }
+    }
+    ba[--n] = 0;
+    var rng = new SecureRandom();
+    var x = [];
+    while (n > 2) { // random non-zero pad
+        x[0] = 0;
+        while (x[0] == 0) {
+            rng.nextBytes(x);
+        }
+        ba[--n] = x[0];
+    }
+    ba[--n] = 2;
+    ba[--n] = 0;
+    return new BigInteger(ba);
+}
+// "empty" RSA key constructor
+var RSAKey = /** @class */ (function () {
+    function RSAKey() {
+        this.n = null;
+        this.e = 0;
+        this.d = null;
+        this.p = null;
+        this.q = null;
+        this.dmp1 = null;
+        this.dmq1 = null;
+        this.coeff = null;
+    }
+    //#region PROTECTED
+    // protected
+    // RSAKey.prototype.doPublic = RSADoPublic;
+    // Perform raw public operation on "x": return x^e (mod n)
+    RSAKey.prototype.doPublic = function (x) {
+        return x.modPowInt(this.e, this.n);
+    };
+    // RSAKey.prototype.doPrivate = RSADoPrivate;
+    // Perform raw private operation on "x": return x^d (mod n)
+    RSAKey.prototype.doPrivate = function (x) {
+        if (this.p == null || this.q == null) {
+            return x.modPow(this.d, this.n);
+        }
+        // TODO: re-calculate any missing CRT params
+        var xp = x.mod(this.p).modPow(this.dmp1, this.p);
+        var xq = x.mod(this.q).modPow(this.dmq1, this.q);
+        while (xp.compareTo(xq) < 0) {
+            xp = xp.add(this.p);
+        }
+        return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);
+    };
+    //#endregion PROTECTED
+    //#region PUBLIC
+    // RSAKey.prototype.setPublic = RSASetPublic;
+    // Set the public key fields N and e from hex strings
+    RSAKey.prototype.setPublic = function (N, E) {
+        if (N != null && E != null && N.length > 0 && E.length > 0) {
+            this.n = parseBigInt(N, 16);
+            this.e = parseInt(E, 16);
+        }
+        else {
+            console.error("Invalid RSA public key");
+        }
+    };
+    // RSAKey.prototype.encrypt = RSAEncrypt;
+    // Return the PKCS#1 RSA encryption of "text" as an even-length hex string
+    RSAKey.prototype.encrypt = function (text) {
+        var m = pkcs1pad2(text, (this.n.bitLength() + 7) >> 3);
+        if (m == null) {
+            return null;
+        }
+        var c = this.doPublic(m);
+        if (c == null) {
+            return null;
+        }
+        var h = c.toString(16);
+        if ((h.length & 1) == 0) {
+            return h;
+        }
+        else {
+            return "0" + h;
+        }
+    };
+    // RSAKey.prototype.setPrivate = RSASetPrivate;
+    // Set the private key fields N, e, and d from hex strings
+    RSAKey.prototype.setPrivate = function (N, E, D) {
+        if (N != null && E != null && N.length > 0 && E.length > 0) {
+            this.n = parseBigInt(N, 16);
+            this.e = parseInt(E, 16);
+            this.d = parseBigInt(D, 16);
+        }
+        else {
+            console.error("Invalid RSA private key");
+        }
+    };
+    // RSAKey.prototype.setPrivateEx = RSASetPrivateEx;
+    // Set the private key fields N, e, d and CRT params from hex strings
+    RSAKey.prototype.setPrivateEx = function (N, E, D, P, Q, DP, DQ, C) {
+        if (N != null && E != null && N.length > 0 && E.length > 0) {
+            this.n = parseBigInt(N, 16);
+            this.e = parseInt(E, 16);
+            this.d = parseBigInt(D, 16);
+            this.p = parseBigInt(P, 16);
+            this.q = parseBigInt(Q, 16);
+            this.dmp1 = parseBigInt(DP, 16);
+            this.dmq1 = parseBigInt(DQ, 16);
+            this.coeff = parseBigInt(C, 16);
+        }
+        else {
+            console.error("Invalid RSA private key");
+        }
+    };
+    // RSAKey.prototype.generate = RSAGenerate;
+    // Generate a new random private key B bits long, using public expt E
+    RSAKey.prototype.generate = function (B, E) {
+        var rng = new SecureRandom();
+        var qs = B >> 1;
+        this.e = parseInt(E, 16);
+        var ee = new BigInteger(E, 16);
+        for (;;) {
+            for (;;) {
+                this.p = new BigInteger(B - qs, 1, rng);
+                if (this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) {
+                    break;
+                }
+            }
+            for (;;) {
+                this.q = new BigInteger(qs, 1, rng);
+                if (this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) {
+                    break;
+                }
+            }
+            if (this.p.compareTo(this.q) <= 0) {
+                var t = this.p;
+                this.p = this.q;
+                this.q = t;
+            }
+            var p1 = this.p.subtract(BigInteger.ONE);
+            var q1 = this.q.subtract(BigInteger.ONE);
+            var phi = p1.multiply(q1);
+            if (phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
+                this.n = this.p.multiply(this.q);
+                this.d = ee.modInverse(phi);
+                this.dmp1 = this.d.mod(p1);
+                this.dmq1 = this.d.mod(q1);
+                this.coeff = this.q.modInverse(this.p);
+                break;
+            }
+        }
+    };
+    // RSAKey.prototype.decrypt = RSADecrypt;
+    // Return the PKCS#1 RSA decryption of "ctext".
+    // "ctext" is an even-length hex string and the output is a plain string.
+    RSAKey.prototype.decrypt = function (ctext) {
+        var c = parseBigInt(ctext, 16);
+        var m = this.doPrivate(c);
+        if (m == null) {
+            return null;
+        }
+        return pkcs1unpad2(m, (this.n.bitLength() + 7) >> 3);
+    };
+    // Generate a new random private key B bits long, using public expt E
+    RSAKey.prototype.generateAsync = function (B, E, callback) {
+        var rng = new SecureRandom();
+        var qs = B >> 1;
+        this.e = parseInt(E, 16);
+        var ee = new BigInteger(E, 16);
+        var rsa = this;
+        // These functions have non-descript names because they were originally for(;;) loops.
+        // I don't know about cryptography to give them better names than loop1-4.
+        var loop1 = function () {
+            var loop4 = function () {
+                if (rsa.p.compareTo(rsa.q) <= 0) {
+                    var t = rsa.p;
+                    rsa.p = rsa.q;
+                    rsa.q = t;
+                }
+                var p1 = rsa.p.subtract(BigInteger.ONE);
+                var q1 = rsa.q.subtract(BigInteger.ONE);
+                var phi = p1.multiply(q1);
+                if (phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
+                    rsa.n = rsa.p.multiply(rsa.q);
+                    rsa.d = ee.modInverse(phi);
+                    rsa.dmp1 = rsa.d.mod(p1);
+                    rsa.dmq1 = rsa.d.mod(q1);
+                    rsa.coeff = rsa.q.modInverse(rsa.p);
+                    setTimeout(function () { callback(); }, 0); // escape
+                }
+                else {
+                    setTimeout(loop1, 0);
+                }
+            };
+            var loop3 = function () {
+                rsa.q = nbi();
+                rsa.q.fromNumberAsync(qs, 1, rng, function () {
+                    rsa.q.subtract(BigInteger.ONE).gcda(ee, function (r) {
+                        if (r.compareTo(BigInteger.ONE) == 0 && rsa.q.isProbablePrime(10)) {
+                            setTimeout(loop4, 0);
+                        }
+                        else {
+                            setTimeout(loop3, 0);
+                        }
+                    });
+                });
+            };
+            var loop2 = function () {
+                rsa.p = nbi();
+                rsa.p.fromNumberAsync(B - qs, 1, rng, function () {
+                    rsa.p.subtract(BigInteger.ONE).gcda(ee, function (r) {
+                        if (r.compareTo(BigInteger.ONE) == 0 && rsa.p.isProbablePrime(10)) {
+                            setTimeout(loop3, 0);
+                        }
+                        else {
+                            setTimeout(loop2, 0);
+                        }
+                    });
+                });
+            };
+            setTimeout(loop2, 0);
+        };
+        setTimeout(loop1, 0);
+    };
+    RSAKey.prototype.sign = function (text, digestMethod, digestName) {
+        var header = getDigestHeader(digestName);
+        var digest = header + digestMethod(text).toString();
+        var m = pkcs1pad1(digest, this.n.bitLength() / 4);
+        if (m == null) {
+            return null;
+        }
+        var c = this.doPrivate(m);
+        if (c == null) {
+            return null;
+        }
+        var h = c.toString(16);
+        if ((h.length & 1) == 0) {
+            return h;
+        }
+        else {
+            return "0" + h;
+        }
+    };
+    RSAKey.prototype.verify = function (text, signature, digestMethod) {
+        var c = parseBigInt(signature, 16);
+        var m = this.doPublic(c);
+        if (m == null) {
+            return null;
+        }
+        var unpadded = m.toString(16).replace(/^1f+00/, "");
+        var digest = removeDigestHeader(unpadded);
+        return digest == digestMethod(text).toString();
+    };
+    return RSAKey;
+}());
+// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext
+function pkcs1unpad2(d, n) {
+    var b = d.toByteArray();
+    var i = 0;
+    while (i < b.length && b[i] == 0) {
+        ++i;
+    }
+    if (b.length - i != n - 1 || b[i] != 2) {
+        return null;
+    }
+    ++i;
+    while (b[i] != 0) {
+        if (++i >= b.length) {
+            return null;
+        }
+    }
+    var ret = "";
+    while (++i < b.length) {
+        var c = b[i] & 255;
+        if (c < 128) { // utf-8 decode
+            ret += String.fromCharCode(c);
+        }
+        else if ((c > 191) && (c < 224)) {
+            ret += String.fromCharCode(((c & 31) << 6) | (b[i + 1] & 63));
+            ++i;
+        }
+        else {
+            ret += String.fromCharCode(((c & 15) << 12) | ((b[i + 1] & 63) << 6) | (b[i + 2] & 63));
+            i += 2;
+        }
+    }
+    return ret;
+}
+// https://tools.ietf.org/html/rfc3447#page-43
+var DIGEST_HEADERS = {
+    md2: "3020300c06082a864886f70d020205000410",
+    md5: "3020300c06082a864886f70d020505000410",
+    sha1: "3021300906052b0e03021a05000414",
+    sha224: "302d300d06096086480165030402040500041c",
+    sha256: "3031300d060960864801650304020105000420",
+    sha384: "3041300d060960864801650304020205000430",
+    sha512: "3051300d060960864801650304020305000440",
+    ripemd160: "3021300906052b2403020105000414",
+};
+function getDigestHeader(name) {
+    return DIGEST_HEADERS[name] || "";
+}
+function removeDigestHeader(str) {
+    for (var name_1 in DIGEST_HEADERS) {
+        if (DIGEST_HEADERS.hasOwnProperty(name_1)) {
+            var header = DIGEST_HEADERS[name_1];
+            var len = header.length;
+            if (str.substr(0, len) == header) {
+                return str.substr(len);
+            }
+        }
+    }
+    return str;
+}
+// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
+// function RSAEncryptB64(text) {
+//  var h = this.encrypt(text);
+//  if(h) return hex2b64(h); else return null;
+// }
+// public
+// RSAKey.prototype.encrypt_b64 = RSAEncryptB64;
+
+/*!
+Copyright (c) 2011, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.9.0
+*/
+var YAHOO = {};
+YAHOO.lang = {
+    /**
+     * Utility to set up the prototype, constructor and superclass properties to
+     * support an inheritance strategy that can chain constructors and methods.
+     * Static members will not be inherited.
+     *
+     * @method extend
+     * @static
+     * @param {Function} subc   the object to modify
+     * @param {Function} superc the object to inherit
+     * @param {Object} overrides  additional properties/methods to add to the
+     *                              subclass prototype.  These will override the
+     *                              matching items obtained from the superclass
+     *                              if present.
+     */
+    extend: function(subc, superc, overrides) {
+        if (! superc || ! subc) {
+            throw new Error("YAHOO.lang.extend failed, please check that " +
+                "all dependencies are included.");
+        }
+
+        var F = function() {};
+        F.prototype = superc.prototype;
+        subc.prototype = new F();
+        subc.prototype.constructor = subc;
+        subc.superclass = superc.prototype;
+
+        if (superc.prototype.constructor == Object.prototype.constructor) {
+            superc.prototype.constructor = superc;
+        }
+
+        if (overrides) {
+            var i;
+            for (i in overrides) {
+                subc.prototype[i] = overrides[i];
+            }
+
+            /*
+             * IE will not enumerate native functions in a derived object even if the
+             * function was overridden.  This is a workaround for specific functions
+             * we care about on the Object prototype.
+             * @property _IEEnumFix
+             * @param {Function} r  the object to receive the augmentation
+             * @param {Function} s  the object that supplies the properties to augment
+             * @static
+             * @private
+             */
+            var _IEEnumFix = function() {},
+                ADD = ["toString", "valueOf"];
+            try {
+                if (/MSIE/.test(navigator2.userAgent)) {
+                    _IEEnumFix = function(r, s) {
+                        for (i = 0; i < ADD.length; i = i + 1) {
+                            var fname = ADD[i], f = s[fname];
+                            if (typeof f === 'function' && f != Object.prototype[fname]) {
+                                r[fname] = f;
+                            }
+                        }
+                    };
+                }
+            } catch (ex) {}            _IEEnumFix(subc.prototype, overrides);
+        }
+    }
+};
+
+/* asn1-1.0.13.js (c) 2013-2017 Kenji Urushima | kjur.github.com/jsrsasign/license
+ */
+
+/**
+ * @fileOverview
+ * @name asn1-1.0.js
+ * @author Kenji Urushima kenji.urushima@gmail.com
+ * @version asn1 1.0.13 (2017-Jun-02)
+ * @since jsrsasign 2.1
+ * @license <a href="https://kjur.github.io/jsrsasign/license/">MIT License</a>
+ */
+
+/**
+ * kjur's class library name space
+ * <p>
+ * This name space provides following name spaces:
+ * <ul>
+ * <li>{@link KJUR.asn1} - ASN.1 primitive hexadecimal encoder</li>
+ * <li>{@link KJUR.asn1.x509} - ASN.1 structure for X.509 certificate and CRL</li>
+ * <li>{@link KJUR.crypto} - Java Cryptographic Extension(JCE) style MessageDigest/Signature
+ * class and utilities</li>
+ * </ul>
+ * </p>
+ * NOTE: Please ignore method summary and document of this namespace. This caused by a bug of jsdoc2.
+ * @name KJUR
+ * @namespace kjur's class library name space
+ */
+var KJUR = {};
+
+/**
+ * kjur's ASN.1 class library name space
+ * <p>
+ * This is ITU-T X.690 ASN.1 DER encoder class library and
+ * class structure and methods is very similar to
+ * org.bouncycastle.asn1 package of
+ * well known BouncyCaslte Cryptography Library.
+ * <h4>PROVIDING ASN.1 PRIMITIVES</h4>
+ * Here are ASN.1 DER primitive classes.
+ * <ul>
+ * <li>0x01 {@link KJUR.asn1.DERBoolean}</li>
+ * <li>0x02 {@link KJUR.asn1.DERInteger}</li>
+ * <li>0x03 {@link KJUR.asn1.DERBitString}</li>
+ * <li>0x04 {@link KJUR.asn1.DEROctetString}</li>
+ * <li>0x05 {@link KJUR.asn1.DERNull}</li>
+ * <li>0x06 {@link KJUR.asn1.DERObjectIdentifier}</li>
+ * <li>0x0a {@link KJUR.asn1.DEREnumerated}</li>
+ * <li>0x0c {@link KJUR.asn1.DERUTF8String}</li>
+ * <li>0x12 {@link KJUR.asn1.DERNumericString}</li>
+ * <li>0x13 {@link KJUR.asn1.DERPrintableString}</li>
+ * <li>0x14 {@link KJUR.asn1.DERTeletexString}</li>
+ * <li>0x16 {@link KJUR.asn1.DERIA5String}</li>
+ * <li>0x17 {@link KJUR.asn1.DERUTCTime}</li>
+ * <li>0x18 {@link KJUR.asn1.DERGeneralizedTime}</li>
+ * <li>0x30 {@link KJUR.asn1.DERSequence}</li>
+ * <li>0x31 {@link KJUR.asn1.DERSet}</li>
+ * </ul>
+ * <h4>OTHER ASN.1 CLASSES</h4>
+ * <ul>
+ * <li>{@link KJUR.asn1.ASN1Object}</li>
+ * <li>{@link KJUR.asn1.DERAbstractString}</li>
+ * <li>{@link KJUR.asn1.DERAbstractTime}</li>
+ * <li>{@link KJUR.asn1.DERAbstractStructured}</li>
+ * <li>{@link KJUR.asn1.DERTaggedObject}</li>
+ * </ul>
+ * <h4>SUB NAME SPACES</h4>
+ * <ul>
+ * <li>{@link KJUR.asn1.cades} - CAdES long term signature format</li>
+ * <li>{@link KJUR.asn1.cms} - Cryptographic Message Syntax</li>
+ * <li>{@link KJUR.asn1.csr} - Certificate Signing Request (CSR/PKCS#10)</li>
+ * <li>{@link KJUR.asn1.tsp} - RFC 3161 Timestamping Protocol Format</li>
+ * <li>{@link KJUR.asn1.x509} - RFC 5280 X.509 certificate and CRL</li>
+ * </ul>
+ * </p>
+ * NOTE: Please ignore method summary and document of this namespace.
+ * This caused by a bug of jsdoc2.
+ * @name KJUR.asn1
+ * @namespace
+ */
+if (typeof KJUR.asn1 == "undefined" || !KJUR.asn1) KJUR.asn1 = {};
+
+/**
+ * ASN1 utilities class
+ * @name KJUR.asn1.ASN1Util
+ * @class ASN1 utilities class
+ * @since asn1 1.0.2
+ */
+KJUR.asn1.ASN1Util = new function() {
+    this.integerToByteHex = function(i) {
+        var h = i.toString(16);
+        if ((h.length % 2) == 1) h = '0' + h;
+        return h;
+    };
+    this.bigIntToMinTwosComplementsHex = function(bigIntegerValue) {
+        var h = bigIntegerValue.toString(16);
+        if (h.substr(0, 1) != '-') {
+            if (h.length % 2 == 1) {
+                h = '0' + h;
+            } else {
+                if (! h.match(/^[0-7]/)) {
+                    h = '00' + h;
+                }
+            }
+        } else {
+            var hPos = h.substr(1);
+            var xorLen = hPos.length;
+            if (xorLen % 2 == 1) {
+                xorLen += 1;
+            } else {
+                if (! h.match(/^[0-7]/)) {
+                    xorLen += 2;
+                }
+            }
+            var hMask = '';
+            for (var i = 0; i < xorLen; i++) {
+                hMask += 'f';
+            }
+            var biMask = new BigInteger(hMask, 16);
+            var biNeg = biMask.xor(bigIntegerValue).add(BigInteger.ONE);
+            h = biNeg.toString(16).replace(/^-/, '');
+        }
+        return h;
+    };
+    /**
+     * get PEM string from hexadecimal data and header string
+     * @name getPEMStringFromHex
+     * @memberOf KJUR.asn1.ASN1Util
+     * @function
+     * @param {String} dataHex hexadecimal string of PEM body
+     * @param {String} pemHeader PEM header string (ex. 'RSA PRIVATE KEY')
+     * @return {String} PEM formatted string of input data
+     * @description
+     * This method converts a hexadecimal string to a PEM string with
+     * a specified header. Its line break will be CRLF("\r\n").
+     * @example
+     * var pem  = KJUR.asn1.ASN1Util.getPEMStringFromHex('616161', 'RSA PRIVATE KEY');
+     * // value of pem will be:
+     * -----BEGIN PRIVATE KEY-----
+     * YWFh
+     * -----END PRIVATE KEY-----
+     */
+    this.getPEMStringFromHex = function(dataHex, pemHeader) {
+        return hextopem(dataHex, pemHeader);
+    };
+
+    /**
+     * generate ASN1Object specifed by JSON parameters
+     * @name newObject
+     * @memberOf KJUR.asn1.ASN1Util
+     * @function
+     * @param {Array} param JSON parameter to generate ASN1Object
+     * @return {KJUR.asn1.ASN1Object} generated object
+     * @since asn1 1.0.3
+     * @description
+     * generate any ASN1Object specified by JSON param
+     * including ASN.1 primitive or structured.
+     * Generally 'param' can be described as follows:
+     * <blockquote>
+     * {TYPE-OF-ASNOBJ: ASN1OBJ-PARAMETER}
+     * </blockquote>
+     * 'TYPE-OF-ASN1OBJ' can be one of following symbols:
+     * <ul>
+     * <li>'bool' - DERBoolean</li>
+     * <li>'int' - DERInteger</li>
+     * <li>'bitstr' - DERBitString</li>
+     * <li>'octstr' - DEROctetString</li>
+     * <li>'null' - DERNull</li>
+     * <li>'oid' - DERObjectIdentifier</li>
+     * <li>'enum' - DEREnumerated</li>
+     * <li>'utf8str' - DERUTF8String</li>
+     * <li>'numstr' - DERNumericString</li>
+     * <li>'prnstr' - DERPrintableString</li>
+     * <li>'telstr' - DERTeletexString</li>
+     * <li>'ia5str' - DERIA5String</li>
+     * <li>'utctime' - DERUTCTime</li>
+     * <li>'gentime' - DERGeneralizedTime</li>
+     * <li>'seq' - DERSequence</li>
+     * <li>'set' - DERSet</li>
+     * <li>'tag' - DERTaggedObject</li>
+     * </ul>
+     * @example
+     * newObject({'prnstr': 'aaa'});
+     * newObject({'seq': [{'int': 3}, {'prnstr': 'aaa'}]})
+     * // ASN.1 Tagged Object
+     * newObject({'tag': {'tag': 'a1',
+     *                    'explicit': true,
+     *                    'obj': {'seq': [{'int': 3}, {'prnstr': 'aaa'}]}}});
+     * // more simple representation of ASN.1 Tagged Object
+     * newObject({'tag': ['a1',
+     *                    true,
+     *                    {'seq': [
+     *                      {'int': 3},
+     *                      {'prnstr': 'aaa'}]}
+     *                   ]});
+     */
+    this.newObject = function(param) {
+        var _KJUR = KJUR,
+            _KJUR_asn1 = _KJUR.asn1,
+            _DERBoolean = _KJUR_asn1.DERBoolean,
+            _DERInteger = _KJUR_asn1.DERInteger,
+            _DERBitString = _KJUR_asn1.DERBitString,
+            _DEROctetString = _KJUR_asn1.DEROctetString,
+            _DERNull = _KJUR_asn1.DERNull,
+            _DERObjectIdentifier = _KJUR_asn1.DERObjectIdentifier,
+            _DEREnumerated = _KJUR_asn1.DEREnumerated,
+            _DERUTF8String = _KJUR_asn1.DERUTF8String,
+            _DERNumericString = _KJUR_asn1.DERNumericString,
+            _DERPrintableString = _KJUR_asn1.DERPrintableString,
+            _DERTeletexString = _KJUR_asn1.DERTeletexString,
+            _DERIA5String = _KJUR_asn1.DERIA5String,
+            _DERUTCTime = _KJUR_asn1.DERUTCTime,
+            _DERGeneralizedTime = _KJUR_asn1.DERGeneralizedTime,
+            _DERSequence = _KJUR_asn1.DERSequence,
+            _DERSet = _KJUR_asn1.DERSet,
+            _DERTaggedObject = _KJUR_asn1.DERTaggedObject,
+            _newObject = _KJUR_asn1.ASN1Util.newObject;
+
+        var keys = Object.keys(param);
+        if (keys.length != 1)
+            throw "key of param shall be only one.";
+        var key = keys[0];
+
+        if (":bool:int:bitstr:octstr:null:oid:enum:utf8str:numstr:prnstr:telstr:ia5str:utctime:gentime:seq:set:tag:".indexOf(":" + key + ":") == -1)
+            throw "undefined key: " + key;
+
+        if (key == "bool")    return new _DERBoolean(param[key]);
+        if (key == "int")     return new _DERInteger(param[key]);
+        if (key == "bitstr")  return new _DERBitString(param[key]);
+        if (key == "octstr")  return new _DEROctetString(param[key]);
+        if (key == "null")    return new _DERNull(param[key]);
+        if (key == "oid")     return new _DERObjectIdentifier(param[key]);
+        if (key == "enum")    return new _DEREnumerated(param[key]);
+        if (key == "utf8str") return new _DERUTF8String(param[key]);
+        if (key == "numstr")  return new _DERNumericString(param[key]);
+        if (key == "prnstr")  return new _DERPrintableString(param[key]);
+        if (key == "telstr")  return new _DERTeletexString(param[key]);
+        if (key == "ia5str")  return new _DERIA5String(param[key]);
+        if (key == "utctime") return new _DERUTCTime(param[key]);
+        if (key == "gentime") return new _DERGeneralizedTime(param[key]);
+
+        if (key == "seq") {
+            var paramList = param[key];
+            var a = [];
+            for (var i = 0; i < paramList.length; i++) {
+                var asn1Obj = _newObject(paramList[i]);
+                a.push(asn1Obj);
+            }
+            return new _DERSequence({'array': a});
+        }
+
+        if (key == "set") {
+            var paramList = param[key];
+            var a = [];
+            for (var i = 0; i < paramList.length; i++) {
+                var asn1Obj = _newObject(paramList[i]);
+                a.push(asn1Obj);
+            }
+            return new _DERSet({'array': a});
+        }
+
+        if (key == "tag") {
+            var tagParam = param[key];
+            if (Object.prototype.toString.call(tagParam) === '[object Array]' &&
+                tagParam.length == 3) {
+                var obj = _newObject(tagParam[2]);
+                return new _DERTaggedObject({tag: tagParam[0],
+                    explicit: tagParam[1],
+                    obj: obj});
+            } else {
+                var newParam = {};
+                if (tagParam.explicit !== undefined)
+                    newParam.explicit = tagParam.explicit;
+                if (tagParam.tag !== undefined)
+                    newParam.tag = tagParam.tag;
+                if (tagParam.obj === undefined)
+                    throw "obj shall be specified for 'tag'.";
+                newParam.obj = _newObject(tagParam.obj);
+                return new _DERTaggedObject(newParam);
+            }
+        }
+    };
+
+    /**
+     * get encoded hexadecimal string of ASN1Object specifed by JSON parameters
+     * @name jsonToASN1HEX
+     * @memberOf KJUR.asn1.ASN1Util
+     * @function
+     * @param {Array} param JSON parameter to generate ASN1Object
+     * @return hexadecimal string of ASN1Object
+     * @since asn1 1.0.4
+     * @description
+     * As for ASN.1 object representation of JSON object,
+     * please see {@link newObject}.
+     * @example
+     * jsonToASN1HEX({'prnstr': 'aaa'});
+     */
+    this.jsonToASN1HEX = function(param) {
+        var asn1Obj = this.newObject(param);
+        return asn1Obj.getEncodedHex();
+    };
+};
+
+/**
+ * get dot noted oid number string from hexadecimal value of OID
+ * @name oidHexToInt
+ * @memberOf KJUR.asn1.ASN1Util
+ * @function
+ * @param {String} hex hexadecimal value of object identifier
+ * @return {String} dot noted string of object identifier
+ * @since jsrsasign 4.8.3 asn1 1.0.7
+ * @description
+ * This static method converts from hexadecimal string representation of
+ * ASN.1 value of object identifier to oid number string.
+ * @example
+ * KJUR.asn1.ASN1Util.oidHexToInt('550406') &rarr; "2.5.4.6"
+ */
+KJUR.asn1.ASN1Util.oidHexToInt = function(hex) {
+    var s = "";
+    var i01 = parseInt(hex.substr(0, 2), 16);
+    var i0 = Math.floor(i01 / 40);
+    var i1 = i01 % 40;
+    var s = i0 + "." + i1;
+
+    var binbuf = "";
+    for (var i = 2; i < hex.length; i += 2) {
+        var value = parseInt(hex.substr(i, 2), 16);
+        var bin = ("00000000" + value.toString(2)).slice(- 8);
+        binbuf = binbuf + bin.substr(1, 7);
+        if (bin.substr(0, 1) == "0") {
+            var bi = new BigInteger(binbuf, 2);
+            s = s + "." + bi.toString(10);
+            binbuf = "";
+        }
+    }
+    return s;
+};
+
+/**
+ * get hexadecimal value of object identifier from dot noted oid value
+ * @name oidIntToHex
+ * @memberOf KJUR.asn1.ASN1Util
+ * @function
+ * @param {String} oidString dot noted string of object identifier
+ * @return {String} hexadecimal value of object identifier
+ * @since jsrsasign 4.8.3 asn1 1.0.7
+ * @description
+ * This static method converts from object identifier value string.
+ * to hexadecimal string representation of it.
+ * @example
+ * KJUR.asn1.ASN1Util.oidIntToHex("2.5.4.6") &rarr; "550406"
+ */
+KJUR.asn1.ASN1Util.oidIntToHex = function(oidString) {
+    var itox = function(i) {
+        var h = i.toString(16);
+        if (h.length == 1) h = '0' + h;
+        return h;
+    };
+
+    var roidtox = function(roid) {
+        var h = '';
+        var bi = new BigInteger(roid, 10);
+        var b = bi.toString(2);
+        var padLen = 7 - b.length % 7;
+        if (padLen == 7) padLen = 0;
+        var bPad = '';
+        for (var i = 0; i < padLen; i++) bPad += '0';
+        b = bPad + b;
+        for (var i = 0; i < b.length - 1; i += 7) {
+            var b8 = b.substr(i, 7);
+            if (i != b.length - 7) b8 = '1' + b8;
+            h += itox(parseInt(b8, 2));
+        }
+        return h;
+    };
+
+    if (! oidString.match(/^[0-9.]+$/)) {
+        throw "malformed oid string: " + oidString;
+    }
+    var h = '';
+    var a = oidString.split('.');
+    var i0 = parseInt(a[0]) * 40 + parseInt(a[1]);
+    h += itox(i0);
+    a.splice(0, 2);
+    for (var i = 0; i < a.length; i++) {
+        h += roidtox(a[i]);
+    }
+    return h;
+};
+
+
+// ********************************************************************
+//  Abstract ASN.1 Classes
+// ********************************************************************
+
+// ********************************************************************
+
+/**
+ * base class for ASN.1 DER encoder object
+ * @name KJUR.asn1.ASN1Object
+ * @class base class for ASN.1 DER encoder object
+ * @property {Boolean} isModified flag whether internal data was changed
+ * @property {String} hTLV hexadecimal string of ASN.1 TLV
+ * @property {String} hT hexadecimal string of ASN.1 TLV tag(T)
+ * @property {String} hL hexadecimal string of ASN.1 TLV length(L)
+ * @property {String} hV hexadecimal string of ASN.1 TLV value(V)
+ * @description
+ */
+KJUR.asn1.ASN1Object = function() {
+    var hV = '';
+
+    /**
+     * get hexadecimal ASN.1 TLV length(L) bytes from TLV value(V)
+     * @name getLengthHexFromValue
+     * @memberOf KJUR.asn1.ASN1Object#
+     * @function
+     * @return {String} hexadecimal string of ASN.1 TLV length(L)
+     */
+    this.getLengthHexFromValue = function() {
+        if (typeof this.hV == "undefined" || this.hV == null) {
+            throw "this.hV is null or undefined.";
+        }
+        if (this.hV.length % 2 == 1) {
+            throw "value hex must be even length: n=" + hV.length + ",v=" + this.hV;
+        }
+        var n = this.hV.length / 2;
+        var hN = n.toString(16);
+        if (hN.length % 2 == 1) {
+            hN = "0" + hN;
+        }
+        if (n < 128) {
+            return hN;
+        } else {
+            var hNlen = hN.length / 2;
+            if (hNlen > 15) {
+                throw "ASN.1 length too long to represent by 8x: n = " + n.toString(16);
+            }
+            var head = 128 + hNlen;
+            return head.toString(16) + hN;
+        }
+    };
+
+    /**
+     * get hexadecimal string of ASN.1 TLV bytes
+     * @name getEncodedHex
+     * @memberOf KJUR.asn1.ASN1Object#
+     * @function
+     * @return {String} hexadecimal string of ASN.1 TLV
+     */
+    this.getEncodedHex = function() {
+        if (this.hTLV == null || this.isModified) {
+            this.hV = this.getFreshValueHex();
+            this.hL = this.getLengthHexFromValue();
+            this.hTLV = this.hT + this.hL + this.hV;
+            this.isModified = false;
+            //alert("first time: " + this.hTLV);
+        }
+        return this.hTLV;
+    };
+
+    /**
+     * get hexadecimal string of ASN.1 TLV value(V) bytes
+     * @name getValueHex
+     * @memberOf KJUR.asn1.ASN1Object#
+     * @function
+     * @return {String} hexadecimal string of ASN.1 TLV value(V) bytes
+     */
+    this.getValueHex = function() {
+        this.getEncodedHex();
+        return this.hV;
+    };
+
+    this.getFreshValueHex = function() {
+        return '';
+    };
+};
+
+// == BEGIN DERAbstractString ================================================
+/**
+ * base class for ASN.1 DER string classes
+ * @name KJUR.asn1.DERAbstractString
+ * @class base class for ASN.1 DER string classes
+ * @param {Array} params associative array of parameters (ex. {'str': 'aaa'})
+ * @property {String} s internal string of value
+ * @extends KJUR.asn1.ASN1Object
+ * @description
+ * <br/>
+ * As for argument 'params' for constructor, you can specify one of
+ * following properties:
+ * <ul>
+ * <li>str - specify initial ASN.1 value(V) by a string</li>
+ * <li>hex - specify initial ASN.1 value(V) by a hexadecimal string</li>
+ * </ul>
+ * NOTE: 'params' can be omitted.
+ */
+KJUR.asn1.DERAbstractString = function(params) {
+    KJUR.asn1.DERAbstractString.superclass.constructor.call(this);
+
+    /**
+     * get string value of this string object
+     * @name getString
+     * @memberOf KJUR.asn1.DERAbstractString#
+     * @function
+     * @return {String} string value of this string object
+     */
+    this.getString = function() {
+        return this.s;
+    };
+
+    /**
+     * set value by a string
+     * @name setString
+     * @memberOf KJUR.asn1.DERAbstractString#
+     * @function
+     * @param {String} newS value by a string to set
+     */
+    this.setString = function(newS) {
+        this.hTLV = null;
+        this.isModified = true;
+        this.s = newS;
+        this.hV = stohex(this.s);
+    };
+
+    /**
+     * set value by a hexadecimal string
+     * @name setStringHex
+     * @memberOf KJUR.asn1.DERAbstractString#
+     * @function
+     * @param {String} newHexString value by a hexadecimal string to set
+     */
+    this.setStringHex = function(newHexString) {
+        this.hTLV = null;
+        this.isModified = true;
+        this.s = null;
+        this.hV = newHexString;
+    };
+
+    this.getFreshValueHex = function() {
+        return this.hV;
+    };
+
+    if (typeof params != "undefined") {
+        if (typeof params == "string") {
+            this.setString(params);
+        } else if (typeof params['str'] != "undefined") {
+            this.setString(params['str']);
+        } else if (typeof params['hex'] != "undefined") {
+            this.setStringHex(params['hex']);
+        }
+    }
+};
+YAHOO.lang.extend(KJUR.asn1.DERAbstractString, KJUR.asn1.ASN1Object);
+// == END   DERAbstractString ================================================
+
+// == BEGIN DERAbstractTime ==================================================
+/**
+ * base class for ASN.1 DER Generalized/UTCTime class
+ * @name KJUR.asn1.DERAbstractTime
+ * @class base class for ASN.1 DER Generalized/UTCTime class
+ * @param {Array} params associative array of parameters (ex. {'str': '130430235959Z'})
+ * @extends KJUR.asn1.ASN1Object
+ * @description
+ * @see KJUR.asn1.ASN1Object - superclass
+ */
+KJUR.asn1.DERAbstractTime = function(params) {
+    KJUR.asn1.DERAbstractTime.superclass.constructor.call(this);
+
+    // --- PRIVATE METHODS --------------------
+    this.localDateToUTC = function(d) {
+        utc = d.getTime() + (d.getTimezoneOffset() * 60000);
+        var utcDate = new Date(utc);
+        return utcDate;
+    };
+
+    /*
+     * format date string by Data object
+     * @name formatDate
+     * @memberOf KJUR.asn1.AbstractTime;
+     * @param {Date} dateObject
+     * @param {string} type 'utc' or 'gen'
+     * @param {boolean} withMillis flag for with millisections or not
+     * @description
+     * 'withMillis' flag is supported from asn1 1.0.6.
+     */
+    this.formatDate = function(dateObject, type, withMillis) {
+        var pad = this.zeroPadding;
+        var d = this.localDateToUTC(dateObject);
+        var year = String(d.getFullYear());
+        if (type == 'utc') year = year.substr(2, 2);
+        var month = pad(String(d.getMonth() + 1), 2);
+        var day = pad(String(d.getDate()), 2);
+        var hour = pad(String(d.getHours()), 2);
+        var min = pad(String(d.getMinutes()), 2);
+        var sec = pad(String(d.getSeconds()), 2);
+        var s = year + month + day + hour + min + sec;
+        if (withMillis === true) {
+            var millis = d.getMilliseconds();
+            if (millis != 0) {
+                var sMillis = pad(String(millis), 3);
+                sMillis = sMillis.replace(/[0]+$/, "");
+                s = s + "." + sMillis;
+            }
+        }
+        return s + "Z";
+    };
+
+    this.zeroPadding = function(s, len) {
+        if (s.length >= len) return s;
+        return new Array(len - s.length + 1).join('0') + s;
+    };
+
+    // --- PUBLIC METHODS --------------------
+    /**
+     * get string value of this string object
+     * @name getString
+     * @memberOf KJUR.asn1.DERAbstractTime#
+     * @function
+     * @return {String} string value of this time object
+     */
+    this.getString = function() {
+        return this.s;
+    };
+
+    /**
+     * set value by a string
+     * @name setString
+     * @memberOf KJUR.asn1.DERAbstractTime#
+     * @function
+     * @param {String} newS value by a string to set such like "130430235959Z"
+     */
+    this.setString = function(newS) {
+        this.hTLV = null;
+        this.isModified = true;
+        this.s = newS;
+        this.hV = stohex(newS);
+    };
+
+    /**
+     * set value by a Date object
+     * @name setByDateValue
+     * @memberOf KJUR.asn1.DERAbstractTime#
+     * @function
+     * @param {Integer} year year of date (ex. 2013)
+     * @param {Integer} month month of date between 1 and 12 (ex. 12)
+     * @param {Integer} day day of month
+     * @param {Integer} hour hours of date
+     * @param {Integer} min minutes of date
+     * @param {Integer} sec seconds of date
+     */
+    this.setByDateValue = function(year, month, day, hour, min, sec) {
+        var dateObject = new Date(Date.UTC(year, month - 1, day, hour, min, sec, 0));
+        this.setByDate(dateObject);
+    };
+
+    this.getFreshValueHex = function() {
+        return this.hV;
+    };
+};
+YAHOO.lang.extend(KJUR.asn1.DERAbstractTime, KJUR.asn1.ASN1Object);
+// == END   DERAbstractTime ==================================================
+
+// == BEGIN DERAbstractStructured ============================================
+/**
+ * base class for ASN.1 DER structured class
+ * @name KJUR.asn1.DERAbstractStructured
+ * @class base class for ASN.1 DER structured class
+ * @property {Array} asn1Array internal array of ASN1Object
+ * @extends KJUR.asn1.ASN1Object
+ * @description
+ * @see KJUR.asn1.ASN1Object - superclass
+ */
+KJUR.asn1.DERAbstractStructured = function(params) {
+    KJUR.asn1.DERAbstractString.superclass.constructor.call(this);
+
+    /**
+     * set value by array of ASN1Object
+     * @name setByASN1ObjectArray
+     * @memberOf KJUR.asn1.DERAbstractStructured#
+     * @function
+     * @param {array} asn1ObjectArray array of ASN1Object to set
+     */
+    this.setByASN1ObjectArray = function(asn1ObjectArray) {
+        this.hTLV = null;
+        this.isModified = true;
+        this.asn1Array = asn1ObjectArray;
+    };
+
+    /**
+     * append an ASN1Object to internal array
+     * @name appendASN1Object
+     * @memberOf KJUR.asn1.DERAbstractStructured#
+     * @function
+     * @param {ASN1Object} asn1Object to add
+     */
+    this.appendASN1Object = function(asn1Object) {
+        this.hTLV = null;
+        this.isModified = true;
+        this.asn1Array.push(asn1Object);
+    };
+
+    this.asn1Array = new Array();
+    if (typeof params != "undefined") {
+        if (typeof params['array'] != "undefined") {
+            this.asn1Array = params['array'];
+        }
+    }
+};
+YAHOO.lang.extend(KJUR.asn1.DERAbstractStructured, KJUR.asn1.ASN1Object);
+
+
+// ********************************************************************
+//  ASN.1 Object Classes
+// ********************************************************************
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER Boolean
+ * @name KJUR.asn1.DERBoolean
+ * @class class for ASN.1 DER Boolean
+ * @extends KJUR.asn1.ASN1Object
+ * @description
+ * @see KJUR.asn1.ASN1Object - superclass
+ */
+KJUR.asn1.DERBoolean = function() {
+    KJUR.asn1.DERBoolean.superclass.constructor.call(this);
+    this.hT = "01";
+    this.hTLV = "0101ff";
+};
+YAHOO.lang.extend(KJUR.asn1.DERBoolean, KJUR.asn1.ASN1Object);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER Integer
+ * @name KJUR.asn1.DERInteger
+ * @class class for ASN.1 DER Integer
+ * @extends KJUR.asn1.ASN1Object
+ * @description
+ * <br/>
+ * As for argument 'params' for constructor, you can specify one of
+ * following properties:
+ * <ul>
+ * <li>int - specify initial ASN.1 value(V) by integer value</li>
+ * <li>bigint - specify initial ASN.1 value(V) by BigInteger object</li>
+ * <li>hex - specify initial ASN.1 value(V) by a hexadecimal string</li>
+ * </ul>
+ * NOTE: 'params' can be omitted.
+ */
+KJUR.asn1.DERInteger = function(params) {
+    KJUR.asn1.DERInteger.superclass.constructor.call(this);
+    this.hT = "02";
+
+    /**
+     * set value by Tom Wu's BigInteger object
+     * @name setByBigInteger
+     * @memberOf KJUR.asn1.DERInteger#
+     * @function
+     * @param {BigInteger} bigIntegerValue to set
+     */
+    this.setByBigInteger = function(bigIntegerValue) {
+        this.hTLV = null;
+        this.isModified = true;
+        this.hV = KJUR.asn1.ASN1Util.bigIntToMinTwosComplementsHex(bigIntegerValue);
+    };
+
+    /**
+     * set value by integer value
+     * @name setByInteger
+     * @memberOf KJUR.asn1.DERInteger
+     * @function
+     * @param {Integer} integer value to set
+     */
+    this.setByInteger = function(intValue) {
+        var bi = new BigInteger(String(intValue), 10);
+        this.setByBigInteger(bi);
+    };
+
+    /**
+     * set value by integer value
+     * @name setValueHex
+     * @memberOf KJUR.asn1.DERInteger#
+     * @function
+     * @param {String} hexadecimal string of integer value
+     * @description
+     * <br/>
+     * NOTE: Value shall be represented by minimum octet length of
+     * two's complement representation.
+     * @example
+     * new KJUR.asn1.DERInteger(123);
+     * new KJUR.asn1.DERInteger({'int': 123});
+     * new KJUR.asn1.DERInteger({'hex': '1fad'});
+     */
+    this.setValueHex = function(newHexString) {
+        this.hV = newHexString;
+    };
+
+    this.getFreshValueHex = function() {
+        return this.hV;
+    };
+
+    if (typeof params != "undefined") {
+        if (typeof params['bigint'] != "undefined") {
+            this.setByBigInteger(params['bigint']);
+        } else if (typeof params['int'] != "undefined") {
+            this.setByInteger(params['int']);
+        } else if (typeof params == "number") {
+            this.setByInteger(params);
+        } else if (typeof params['hex'] != "undefined") {
+            this.setValueHex(params['hex']);
+        }
+    }
+};
+YAHOO.lang.extend(KJUR.asn1.DERInteger, KJUR.asn1.ASN1Object);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER encoded BitString primitive
+ * @name KJUR.asn1.DERBitString
+ * @class class for ASN.1 DER encoded BitString primitive
+ * @extends KJUR.asn1.ASN1Object
+ * @description
+ * <br/>
+ * As for argument 'params' for constructor, you can specify one of
+ * following properties:
+ * <ul>
+ * <li>bin - specify binary string (ex. '10111')</li>
+ * <li>array - specify array of boolean (ex. [true,false,true,true])</li>
+ * <li>hex - specify hexadecimal string of ASN.1 value(V) including unused bits</li>
+ * <li>obj - specify {@link KJUR.asn1.ASN1Util.newObject}
+ * argument for "BitString encapsulates" structure.</li>
+ * </ul>
+ * NOTE1: 'params' can be omitted.<br/>
+ * NOTE2: 'obj' parameter have been supported since
+ * asn1 1.0.11, jsrsasign 6.1.1 (2016-Sep-25).<br/>
+ * @example
+ * // default constructor
+ * o = new KJUR.asn1.DERBitString();
+ * // initialize with binary string
+ * o = new KJUR.asn1.DERBitString({bin: "1011"});
+ * // initialize with boolean array
+ * o = new KJUR.asn1.DERBitString({array: [true,false,true,true]});
+ * // initialize with hexadecimal string (04 is unused bits)
+ * o = new KJUR.asn1.DEROctetString({hex: "04bac0"});
+ * // initialize with ASN1Util.newObject argument for encapsulated
+ * o = new KJUR.asn1.DERBitString({obj: {seq: [{int: 3}, {prnstr: 'aaa'}]}});
+ * // above generates a ASN.1 data like this:
+ * // BIT STRING, encapsulates {
+ * //   SEQUENCE {
+ * //     INTEGER 3
+ * //     PrintableString 'aaa'
+ * //     }
+ * //   }
+ */
+KJUR.asn1.DERBitString = function(params) {
+    if (params !== undefined && typeof params.obj !== "undefined") {
+        var o = KJUR.asn1.ASN1Util.newObject(params.obj);
+        params.hex = "00" + o.getEncodedHex();
+    }
+    KJUR.asn1.DERBitString.superclass.constructor.call(this);
+    this.hT = "03";
+
+    /**
+     * set ASN.1 value(V) by a hexadecimal string including unused bits
+     * @name setHexValueIncludingUnusedBits
+     * @memberOf KJUR.asn1.DERBitString#
+     * @function
+     * @param {String} newHexStringIncludingUnusedBits
+     */
+    this.setHexValueIncludingUnusedBits = function(newHexStringIncludingUnusedBits) {
+        this.hTLV = null;
+        this.isModified = true;
+        this.hV = newHexStringIncludingUnusedBits;
+    };
+
+    /**
+     * set ASN.1 value(V) by unused bit and hexadecimal string of value
+     * @name setUnusedBitsAndHexValue
+     * @memberOf KJUR.asn1.DERBitString#
+     * @function
+     * @param {Integer} unusedBits
+     * @param {String} hValue
+     */
+    this.setUnusedBitsAndHexValue = function(unusedBits, hValue) {
+        if (unusedBits < 0 || 7 < unusedBits) {
+            throw "unused bits shall be from 0 to 7: u = " + unusedBits;
+        }
+        var hUnusedBits = "0" + unusedBits;
+        this.hTLV = null;
+        this.isModified = true;
+        this.hV = hUnusedBits + hValue;
+    };
+
+    /**
+     * set ASN.1 DER BitString by binary string<br/>
+     * @name setByBinaryString
+     * @memberOf KJUR.asn1.DERBitString#
+     * @function
+     * @param {String} binaryString binary value string (i.e. '10111')
+     * @description
+     * Its unused bits will be calculated automatically by length of
+     * 'binaryValue'. <br/>
+     * NOTE: Trailing zeros '0' will be ignored.
+     * @example
+     * o = new KJUR.asn1.DERBitString();
+     * o.setByBooleanArray("01011");
+     */
+    this.setByBinaryString = function(binaryString) {
+        binaryString = binaryString.replace(/0+$/, '');
+        var unusedBits = 8 - binaryString.length % 8;
+        if (unusedBits == 8) unusedBits = 0;
+        for (var i = 0; i <= unusedBits; i++) {
+            binaryString += '0';
+        }
+        var h = '';
+        for (var i = 0; i < binaryString.length - 1; i += 8) {
+            var b = binaryString.substr(i, 8);
+            var x = parseInt(b, 2).toString(16);
+            if (x.length == 1) x = '0' + x;
+            h += x;
+        }
+        this.hTLV = null;
+        this.isModified = true;
+        this.hV = '0' + unusedBits + h;
+    };
+
+    /**
+     * set ASN.1 TLV value(V) by an array of boolean<br/>
+     * @name setByBooleanArray
+     * @memberOf KJUR.asn1.DERBitString#
+     * @function
+     * @param {array} booleanArray array of boolean (ex. [true, false, true])
+     * @description
+     * NOTE: Trailing falses will be ignored in the ASN.1 DER Object.
+     * @example
+     * o = new KJUR.asn1.DERBitString();
+     * o.setByBooleanArray([false, true, false, true, true]);
+     */
+    this.setByBooleanArray = function(booleanArray) {
+        var s = '';
+        for (var i = 0; i < booleanArray.length; i++) {
+            if (booleanArray[i] == true) {
+                s += '1';
+            } else {
+                s += '0';
+            }
+        }
+        this.setByBinaryString(s);
+    };
+
+    /**
+     * generate an array of falses with specified length<br/>
+     * @name newFalseArray
+     * @memberOf KJUR.asn1.DERBitString
+     * @function
+     * @param {Integer} nLength length of array to generate
+     * @return {array} array of boolean falses
+     * @description
+     * This static method may be useful to initialize boolean array.
+     * @example
+     * o = new KJUR.asn1.DERBitString();
+     * o.newFalseArray(3) &rarr; [false, false, false]
+     */
+    this.newFalseArray = function(nLength) {
+        var a = new Array(nLength);
+        for (var i = 0; i < nLength; i++) {
+            a[i] = false;
+        }
+        return a;
+    };
+
+    this.getFreshValueHex = function() {
+        return this.hV;
+    };
+
+    if (typeof params != "undefined") {
+        if (typeof params == "string" && params.toLowerCase().match(/^[0-9a-f]+$/)) {
+            this.setHexValueIncludingUnusedBits(params);
+        } else if (typeof params['hex'] != "undefined") {
+            this.setHexValueIncludingUnusedBits(params['hex']);
+        } else if (typeof params['bin'] != "undefined") {
+            this.setByBinaryString(params['bin']);
+        } else if (typeof params['array'] != "undefined") {
+            this.setByBooleanArray(params['array']);
+        }
+    }
+};
+YAHOO.lang.extend(KJUR.asn1.DERBitString, KJUR.asn1.ASN1Object);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER OctetString<br/>
+ * @name KJUR.asn1.DEROctetString
+ * @class class for ASN.1 DER OctetString
+ * @param {Array} params associative array of parameters (ex. {'str': 'aaa'})
+ * @extends KJUR.asn1.DERAbstractString
+ * @description
+ * This class provides ASN.1 OctetString simple type.<br/>
+ * Supported "params" attributes are:
+ * <ul>
+ * <li>str - to set a string as a value</li>
+ * <li>hex - to set a hexadecimal string as a value</li>
+ * <li>obj - to set a encapsulated ASN.1 value by JSON object
+ * which is defined in {@link KJUR.asn1.ASN1Util.newObject}</li>
+ * </ul>
+ * NOTE: A parameter 'obj' have been supported
+ * for "OCTET STRING, encapsulates" structure.
+ * since asn1 1.0.11, jsrsasign 6.1.1 (2016-Sep-25).
+ * @see KJUR.asn1.DERAbstractString - superclass
+ * @example
+ * // default constructor
+ * o = new KJUR.asn1.DEROctetString();
+ * // initialize with string
+ * o = new KJUR.asn1.DEROctetString({str: "aaa"});
+ * // initialize with hexadecimal string
+ * o = new KJUR.asn1.DEROctetString({hex: "616161"});
+ * // initialize with ASN1Util.newObject argument
+ * o = new KJUR.asn1.DEROctetString({obj: {seq: [{int: 3}, {prnstr: 'aaa'}]}});
+ * // above generates a ASN.1 data like this:
+ * // OCTET STRING, encapsulates {
+ * //   SEQUENCE {
+ * //     INTEGER 3
+ * //     PrintableString 'aaa'
+ * //     }
+ * //   }
+ */
+KJUR.asn1.DEROctetString = function(params) {
+    if (params !== undefined && typeof params.obj !== "undefined") {
+        var o = KJUR.asn1.ASN1Util.newObject(params.obj);
+        params.hex = o.getEncodedHex();
+    }
+    KJUR.asn1.DEROctetString.superclass.constructor.call(this, params);
+    this.hT = "04";
+};
+YAHOO.lang.extend(KJUR.asn1.DEROctetString, KJUR.asn1.DERAbstractString);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER Null
+ * @name KJUR.asn1.DERNull
+ * @class class for ASN.1 DER Null
+ * @extends KJUR.asn1.ASN1Object
+ * @description
+ * @see KJUR.asn1.ASN1Object - superclass
+ */
+KJUR.asn1.DERNull = function() {
+    KJUR.asn1.DERNull.superclass.constructor.call(this);
+    this.hT = "05";
+    this.hTLV = "0500";
+};
+YAHOO.lang.extend(KJUR.asn1.DERNull, KJUR.asn1.ASN1Object);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER ObjectIdentifier
+ * @name KJUR.asn1.DERObjectIdentifier
+ * @class class for ASN.1 DER ObjectIdentifier
+ * @param {Array} params associative array of parameters (ex. {'oid': '2.5.4.5'})
+ * @extends KJUR.asn1.ASN1Object
+ * @description
+ * <br/>
+ * As for argument 'params' for constructor, you can specify one of
+ * following properties:
+ * <ul>
+ * <li>oid - specify initial ASN.1 value(V) by a oid string (ex. 2.5.4.13)</li>
+ * <li>hex - specify initial ASN.1 value(V) by a hexadecimal string</li>
+ * </ul>
+ * NOTE: 'params' can be omitted.
+ */
+KJUR.asn1.DERObjectIdentifier = function(params) {
+    var itox = function(i) {
+        var h = i.toString(16);
+        if (h.length == 1) h = '0' + h;
+        return h;
+    };
+    var roidtox = function(roid) {
+        var h = '';
+        var bi = new BigInteger(roid, 10);
+        var b = bi.toString(2);
+        var padLen = 7 - b.length % 7;
+        if (padLen == 7) padLen = 0;
+        var bPad = '';
+        for (var i = 0; i < padLen; i++) bPad += '0';
+        b = bPad + b;
+        for (var i = 0; i < b.length - 1; i += 7) {
+            var b8 = b.substr(i, 7);
+            if (i != b.length - 7) b8 = '1' + b8;
+            h += itox(parseInt(b8, 2));
+        }
+        return h;
+    };
+
+    KJUR.asn1.DERObjectIdentifier.superclass.constructor.call(this);
+    this.hT = "06";
+
+    /**
+     * set value by a hexadecimal string
+     * @name setValueHex
+     * @memberOf KJUR.asn1.DERObjectIdentifier#
+     * @function
+     * @param {String} newHexString hexadecimal value of OID bytes
+     */
+    this.setValueHex = function(newHexString) {
+        this.hTLV = null;
+        this.isModified = true;
+        this.s = null;
+        this.hV = newHexString;
+    };
+
+    /**
+     * set value by a OID string<br/>
+     * @name setValueOidString
+     * @memberOf KJUR.asn1.DERObjectIdentifier#
+     * @function
+     * @param {String} oidString OID string (ex. 2.5.4.13)
+     * @example
+     * o = new KJUR.asn1.DERObjectIdentifier();
+     * o.setValueOidString("2.5.4.13");
+     */
+    this.setValueOidString = function(oidString) {
+        if (! oidString.match(/^[0-9.]+$/)) {
+            throw "malformed oid string: " + oidString;
+        }
+        var h = '';
+        var a = oidString.split('.');
+        var i0 = parseInt(a[0]) * 40 + parseInt(a[1]);
+        h += itox(i0);
+        a.splice(0, 2);
+        for (var i = 0; i < a.length; i++) {
+            h += roidtox(a[i]);
+        }
+        this.hTLV = null;
+        this.isModified = true;
+        this.s = null;
+        this.hV = h;
+    };
+
+    /**
+     * set value by a OID name
+     * @name setValueName
+     * @memberOf KJUR.asn1.DERObjectIdentifier#
+     * @function
+     * @param {String} oidName OID name (ex. 'serverAuth')
+     * @since 1.0.1
+     * @description
+     * OID name shall be defined in 'KJUR.asn1.x509.OID.name2oidList'.
+     * Otherwise raise error.
+     * @example
+     * o = new KJUR.asn1.DERObjectIdentifier();
+     * o.setValueName("serverAuth");
+     */
+    this.setValueName = function(oidName) {
+        var oid = KJUR.asn1.x509.OID.name2oid(oidName);
+        if (oid !== '') {
+            this.setValueOidString(oid);
+        } else {
+            throw "DERObjectIdentifier oidName undefined: " + oidName;
+        }
+    };
+
+    this.getFreshValueHex = function() {
+        return this.hV;
+    };
+
+    if (params !== undefined) {
+        if (typeof params === "string") {
+            if (params.match(/^[0-2].[0-9.]+$/)) {
+                this.setValueOidString(params);
+            } else {
+                this.setValueName(params);
+            }
+        } else if (params.oid !== undefined) {
+            this.setValueOidString(params.oid);
+        } else if (params.hex !== undefined) {
+            this.setValueHex(params.hex);
+        } else if (params.name !== undefined) {
+            this.setValueName(params.name);
+        }
+    }
+};
+YAHOO.lang.extend(KJUR.asn1.DERObjectIdentifier, KJUR.asn1.ASN1Object);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER Enumerated
+ * @name KJUR.asn1.DEREnumerated
+ * @class class for ASN.1 DER Enumerated
+ * @extends KJUR.asn1.ASN1Object
+ * @description
+ * <br/>
+ * As for argument 'params' for constructor, you can specify one of
+ * following properties:
+ * <ul>
+ * <li>int - specify initial ASN.1 value(V) by integer value</li>
+ * <li>hex - specify initial ASN.1 value(V) by a hexadecimal string</li>
+ * </ul>
+ * NOTE: 'params' can be omitted.
+ * @example
+ * new KJUR.asn1.DEREnumerated(123);
+ * new KJUR.asn1.DEREnumerated({int: 123});
+ * new KJUR.asn1.DEREnumerated({hex: '1fad'});
+ */
+KJUR.asn1.DEREnumerated = function(params) {
+    KJUR.asn1.DEREnumerated.superclass.constructor.call(this);
+    this.hT = "0a";
+
+    /**
+     * set value by Tom Wu's BigInteger object
+     * @name setByBigInteger
+     * @memberOf KJUR.asn1.DEREnumerated#
+     * @function
+     * @param {BigInteger} bigIntegerValue to set
+     */
+    this.setByBigInteger = function(bigIntegerValue) {
+        this.hTLV = null;
+        this.isModified = true;
+        this.hV = KJUR.asn1.ASN1Util.bigIntToMinTwosComplementsHex(bigIntegerValue);
+    };
+
+    /**
+     * set value by integer value
+     * @name setByInteger
+     * @memberOf KJUR.asn1.DEREnumerated#
+     * @function
+     * @param {Integer} integer value to set
+     */
+    this.setByInteger = function(intValue) {
+        var bi = new BigInteger(String(intValue), 10);
+        this.setByBigInteger(bi);
+    };
+
+    /**
+     * set value by integer value
+     * @name setValueHex
+     * @memberOf KJUR.asn1.DEREnumerated#
+     * @function
+     * @param {String} hexadecimal string of integer value
+     * @description
+     * <br/>
+     * NOTE: Value shall be represented by minimum octet length of
+     * two's complement representation.
+     */
+    this.setValueHex = function(newHexString) {
+        this.hV = newHexString;
+    };
+
+    this.getFreshValueHex = function() {
+        return this.hV;
+    };
+
+    if (typeof params != "undefined") {
+        if (typeof params['int'] != "undefined") {
+            this.setByInteger(params['int']);
+        } else if (typeof params == "number") {
+            this.setByInteger(params);
+        } else if (typeof params['hex'] != "undefined") {
+            this.setValueHex(params['hex']);
+        }
+    }
+};
+YAHOO.lang.extend(KJUR.asn1.DEREnumerated, KJUR.asn1.ASN1Object);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER UTF8String
+ * @name KJUR.asn1.DERUTF8String
+ * @class class for ASN.1 DER UTF8String
+ * @param {Array} params associative array of parameters (ex. {'str': 'aaa'})
+ * @extends KJUR.asn1.DERAbstractString
+ * @description
+ * @see KJUR.asn1.DERAbstractString - superclass
+ */
+KJUR.asn1.DERUTF8String = function(params) {
+    KJUR.asn1.DERUTF8String.superclass.constructor.call(this, params);
+    this.hT = "0c";
+};
+YAHOO.lang.extend(KJUR.asn1.DERUTF8String, KJUR.asn1.DERAbstractString);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER NumericString
+ * @name KJUR.asn1.DERNumericString
+ * @class class for ASN.1 DER NumericString
+ * @param {Array} params associative array of parameters (ex. {'str': 'aaa'})
+ * @extends KJUR.asn1.DERAbstractString
+ * @description
+ * @see KJUR.asn1.DERAbstractString - superclass
+ */
+KJUR.asn1.DERNumericString = function(params) {
+    KJUR.asn1.DERNumericString.superclass.constructor.call(this, params);
+    this.hT = "12";
+};
+YAHOO.lang.extend(KJUR.asn1.DERNumericString, KJUR.asn1.DERAbstractString);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER PrintableString
+ * @name KJUR.asn1.DERPrintableString
+ * @class class for ASN.1 DER PrintableString
+ * @param {Array} params associative array of parameters (ex. {'str': 'aaa'})
+ * @extends KJUR.asn1.DERAbstractString
+ * @description
+ * @see KJUR.asn1.DERAbstractString - superclass
+ */
+KJUR.asn1.DERPrintableString = function(params) {
+    KJUR.asn1.DERPrintableString.superclass.constructor.call(this, params);
+    this.hT = "13";
+};
+YAHOO.lang.extend(KJUR.asn1.DERPrintableString, KJUR.asn1.DERAbstractString);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER TeletexString
+ * @name KJUR.asn1.DERTeletexString
+ * @class class for ASN.1 DER TeletexString
+ * @param {Array} params associative array of parameters (ex. {'str': 'aaa'})
+ * @extends KJUR.asn1.DERAbstractString
+ * @description
+ * @see KJUR.asn1.DERAbstractString - superclass
+ */
+KJUR.asn1.DERTeletexString = function(params) {
+    KJUR.asn1.DERTeletexString.superclass.constructor.call(this, params);
+    this.hT = "14";
+};
+YAHOO.lang.extend(KJUR.asn1.DERTeletexString, KJUR.asn1.DERAbstractString);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER IA5String
+ * @name KJUR.asn1.DERIA5String
+ * @class class for ASN.1 DER IA5String
+ * @param {Array} params associative array of parameters (ex. {'str': 'aaa'})
+ * @extends KJUR.asn1.DERAbstractString
+ * @description
+ * @see KJUR.asn1.DERAbstractString - superclass
+ */
+KJUR.asn1.DERIA5String = function(params) {
+    KJUR.asn1.DERIA5String.superclass.constructor.call(this, params);
+    this.hT = "16";
+};
+YAHOO.lang.extend(KJUR.asn1.DERIA5String, KJUR.asn1.DERAbstractString);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER UTCTime
+ * @name KJUR.asn1.DERUTCTime
+ * @class class for ASN.1 DER UTCTime
+ * @param {Array} params associative array of parameters (ex. {'str': '130430235959Z'})
+ * @extends KJUR.asn1.DERAbstractTime
+ * @description
+ * <br/>
+ * As for argument 'params' for constructor, you can specify one of
+ * following properties:
+ * <ul>
+ * <li>str - specify initial ASN.1 value(V) by a string (ex.'130430235959Z')</li>
+ * <li>hex - specify initial ASN.1 value(V) by a hexadecimal string</li>
+ * <li>date - specify Date object.</li>
+ * </ul>
+ * NOTE: 'params' can be omitted.
+ * <h4>EXAMPLES</h4>
+ * @example
+ * d1 = new KJUR.asn1.DERUTCTime();
+ * d1.setString('130430125959Z');
+ *
+ * d2 = new KJUR.asn1.DERUTCTime({'str': '130430125959Z'});
+ * d3 = new KJUR.asn1.DERUTCTime({'date': new Date(Date.UTC(2015, 0, 31, 0, 0, 0, 0))});
+ * d4 = new KJUR.asn1.DERUTCTime('130430125959Z');
+ */
+KJUR.asn1.DERUTCTime = function(params) {
+    KJUR.asn1.DERUTCTime.superclass.constructor.call(this, params);
+    this.hT = "17";
+
+    /**
+     * set value by a Date object<br/>
+     * @name setByDate
+     * @memberOf KJUR.asn1.DERUTCTime#
+     * @function
+     * @param {Date} dateObject Date object to set ASN.1 value(V)
+     * @example
+     * o = new KJUR.asn1.DERUTCTime();
+     * o.setByDate(new Date("2016/12/31"));
+     */
+    this.setByDate = function(dateObject) {
+        this.hTLV = null;
+        this.isModified = true;
+        this.date = dateObject;
+        this.s = this.formatDate(this.date, 'utc');
+        this.hV = stohex(this.s);
+    };
+
+    this.getFreshValueHex = function() {
+        if (typeof this.date == "undefined" && typeof this.s == "undefined") {
+            this.date = new Date();
+            this.s = this.formatDate(this.date, 'utc');
+            this.hV = stohex(this.s);
+        }
+        return this.hV;
+    };
+
+    if (params !== undefined) {
+        if (params.str !== undefined) {
+            this.setString(params.str);
+        } else if (typeof params == "string" && params.match(/^[0-9]{12}Z$/)) {
+            this.setString(params);
+        } else if (params.hex !== undefined) {
+            this.setStringHex(params.hex);
+        } else if (params.date !== undefined) {
+            this.setByDate(params.date);
+        }
+    }
+};
+YAHOO.lang.extend(KJUR.asn1.DERUTCTime, KJUR.asn1.DERAbstractTime);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER GeneralizedTime
+ * @name KJUR.asn1.DERGeneralizedTime
+ * @class class for ASN.1 DER GeneralizedTime
+ * @param {Array} params associative array of parameters (ex. {'str': '20130430235959Z'})
+ * @property {Boolean} withMillis flag to show milliseconds or not
+ * @extends KJUR.asn1.DERAbstractTime
+ * @description
+ * <br/>
+ * As for argument 'params' for constructor, you can specify one of
+ * following properties:
+ * <ul>
+ * <li>str - specify initial ASN.1 value(V) by a string (ex.'20130430235959Z')</li>
+ * <li>hex - specify initial ASN.1 value(V) by a hexadecimal string</li>
+ * <li>date - specify Date object.</li>
+ * <li>millis - specify flag to show milliseconds (from 1.0.6)</li>
+ * </ul>
+ * NOTE1: 'params' can be omitted.
+ * NOTE2: 'withMillis' property is supported from asn1 1.0.6.
+ */
+KJUR.asn1.DERGeneralizedTime = function(params) {
+    KJUR.asn1.DERGeneralizedTime.superclass.constructor.call(this, params);
+    this.hT = "18";
+    this.withMillis = false;
+
+    /**
+     * set value by a Date object
+     * @name setByDate
+     * @memberOf KJUR.asn1.DERGeneralizedTime#
+     * @function
+     * @param {Date} dateObject Date object to set ASN.1 value(V)
+     * @example
+     * When you specify UTC time, use 'Date.UTC' method like this:<br/>
+     * o1 = new DERUTCTime();
+     * o1.setByDate(date);
+     *
+     * date = new Date(Date.UTC(2015, 0, 31, 23, 59, 59, 0)); #2015JAN31 23:59:59
+     */
+    this.setByDate = function(dateObject) {
+        this.hTLV = null;
+        this.isModified = true;
+        this.date = dateObject;
+        this.s = this.formatDate(this.date, 'gen', this.withMillis);
+        this.hV = stohex(this.s);
+    };
+
+    this.getFreshValueHex = function() {
+        if (this.date === undefined && this.s === undefined) {
+            this.date = new Date();
+            this.s = this.formatDate(this.date, 'gen', this.withMillis);
+            this.hV = stohex(this.s);
+        }
+        return this.hV;
+    };
+
+    if (params !== undefined) {
+        if (params.str !== undefined) {
+            this.setString(params.str);
+        } else if (typeof params == "string" && params.match(/^[0-9]{14}Z$/)) {
+            this.setString(params);
+        } else if (params.hex !== undefined) {
+            this.setStringHex(params.hex);
+        } else if (params.date !== undefined) {
+            this.setByDate(params.date);
+        }
+        if (params.millis === true) {
+            this.withMillis = true;
+        }
+    }
+};
+YAHOO.lang.extend(KJUR.asn1.DERGeneralizedTime, KJUR.asn1.DERAbstractTime);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER Sequence
+ * @name KJUR.asn1.DERSequence
+ * @class class for ASN.1 DER Sequence
+ * @extends KJUR.asn1.DERAbstractStructured
+ * @description
+ * <br/>
+ * As for argument 'params' for constructor, you can specify one of
+ * following properties:
+ * <ul>
+ * <li>array - specify array of ASN1Object to set elements of content</li>
+ * </ul>
+ * NOTE: 'params' can be omitted.
+ */
+KJUR.asn1.DERSequence = function(params) {
+    KJUR.asn1.DERSequence.superclass.constructor.call(this, params);
+    this.hT = "30";
+    this.getFreshValueHex = function() {
+        var h = '';
+        for (var i = 0; i < this.asn1Array.length; i++) {
+            var asn1Obj = this.asn1Array[i];
+            h += asn1Obj.getEncodedHex();
+        }
+        this.hV = h;
+        return this.hV;
+    };
+};
+YAHOO.lang.extend(KJUR.asn1.DERSequence, KJUR.asn1.DERAbstractStructured);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER Set
+ * @name KJUR.asn1.DERSet
+ * @class class for ASN.1 DER Set
+ * @extends KJUR.asn1.DERAbstractStructured
+ * @description
+ * <br/>
+ * As for argument 'params' for constructor, you can specify one of
+ * following properties:
+ * <ul>
+ * <li>array - specify array of ASN1Object to set elements of content</li>
+ * <li>sortflag - flag for sort (default: true). ASN.1 BER is not sorted in 'SET OF'.</li>
+ * </ul>
+ * NOTE1: 'params' can be omitted.<br/>
+ * NOTE2: sortflag is supported since 1.0.5.
+ */
+KJUR.asn1.DERSet = function(params) {
+    KJUR.asn1.DERSet.superclass.constructor.call(this, params);
+    this.hT = "31";
+    this.sortFlag = true; // item shall be sorted only in ASN.1 DER
+    this.getFreshValueHex = function() {
+        var a = new Array();
+        for (var i = 0; i < this.asn1Array.length; i++) {
+            var asn1Obj = this.asn1Array[i];
+            a.push(asn1Obj.getEncodedHex());
+        }
+        if (this.sortFlag == true) a.sort();
+        this.hV = a.join('');
+        return this.hV;
+    };
+
+    if (typeof params != "undefined") {
+        if (typeof params.sortflag != "undefined" &&
+            params.sortflag == false)
+            this.sortFlag = false;
+    }
+};
+YAHOO.lang.extend(KJUR.asn1.DERSet, KJUR.asn1.DERAbstractStructured);
+
+// ********************************************************************
+/**
+ * class for ASN.1 DER TaggedObject
+ * @name KJUR.asn1.DERTaggedObject
+ * @class class for ASN.1 DER TaggedObject
+ * @extends KJUR.asn1.ASN1Object
+ * @description
+ * <br/>
+ * Parameter 'tagNoNex' is ASN.1 tag(T) value for this object.
+ * For example, if you find '[1]' tag in a ASN.1 dump,
+ * 'tagNoHex' will be 'a1'.
+ * <br/>
+ * As for optional argument 'params' for constructor, you can specify *ANY* of
+ * following properties:
+ * <ul>
+ * <li>explicit - specify true if this is explicit tag otherwise false
+ *     (default is 'true').</li>
+ * <li>tag - specify tag (default is 'a0' which means [0])</li>
+ * <li>obj - specify ASN1Object which is tagged</li>
+ * </ul>
+ * @example
+ * d1 = new KJUR.asn1.DERUTF8String({'str':'a'});
+ * d2 = new KJUR.asn1.DERTaggedObject({'obj': d1});
+ * hex = d2.getEncodedHex();
+ */
+KJUR.asn1.DERTaggedObject = function(params) {
+    KJUR.asn1.DERTaggedObject.superclass.constructor.call(this);
+    this.hT = "a0";
+    this.hV = '';
+    this.isExplicit = true;
+    this.asn1Object = null;
+
+    /**
+     * set value by an ASN1Object
+     * @name setString
+     * @memberOf KJUR.asn1.DERTaggedObject#
+     * @function
+     * @param {Boolean} isExplicitFlag flag for explicit/implicit tag
+     * @param {Integer} tagNoHex hexadecimal string of ASN.1 tag
+     * @param {ASN1Object} asn1Object ASN.1 to encapsulate
+     */
+    this.setASN1Object = function(isExplicitFlag, tagNoHex, asn1Object) {
+        this.hT = tagNoHex;
+        this.isExplicit = isExplicitFlag;
+        this.asn1Object = asn1Object;
+        if (this.isExplicit) {
+            this.hV = this.asn1Object.getEncodedHex();
+            this.hTLV = null;
+            this.isModified = true;
+        } else {
+            this.hV = null;
+            this.hTLV = asn1Object.getEncodedHex();
+            this.hTLV = this.hTLV.replace(/^../, tagNoHex);
+            this.isModified = false;
+        }
+    };
+
+    this.getFreshValueHex = function() {
+        return this.hV;
+    };
+
+    if (typeof params != "undefined") {
+        if (typeof params['tag'] != "undefined") {
+            this.hT = params['tag'];
+        }
+        if (typeof params['explicit'] != "undefined") {
+            this.isExplicit = params['explicit'];
+        }
+        if (typeof params['obj'] != "undefined") {
+            this.asn1Object = params['obj'];
+            this.setASN1Object(this.isExplicit, this.hT, this.asn1Object);
+        }
+    }
+};
+YAHOO.lang.extend(KJUR.asn1.DERTaggedObject, KJUR.asn1.ASN1Object);
+
+/**
+ * Create a new JSEncryptRSAKey that extends Tom Wu's RSA key object.
+ * This object is just a decorator for parsing the key parameter
+ * @param {string|Object} key - The key in string format, or an object containing
+ * the parameters needed to build a RSAKey object.
+ * @constructor
+ */
+var JSEncryptRSAKey = /** @class */ (function (_super) {
+    __extends(JSEncryptRSAKey, _super);
+    function JSEncryptRSAKey(key) {
+        var _this = _super.call(this) || this;
+        // Call the super constructor.
+        //  RSAKey.call(this);
+        // If a key key was provided.
+        if (key) {
+            // If this is a string...
+            if (typeof key === "string") {
+                _this.parseKey(key);
+            }
+            else if (JSEncryptRSAKey.hasPrivateKeyProperty(key) ||
+                JSEncryptRSAKey.hasPublicKeyProperty(key)) {
+                // Set the values for the key.
+                _this.parsePropertiesFrom(key);
+            }
+        }
+        return _this;
+    }
+    /**
+     * Method to parse a pem encoded string containing both a public or private key.
+     * The method will translate the pem encoded string in a der encoded string and
+     * will parse private key and public key parameters. This method accepts public key
+     * in the rsaencryption pkcs #1 format (oid: 1.2.840.113549.1.1.1).
+     *
+     * @todo Check how many rsa formats use the same format of pkcs #1.
+     *
+     * The format is defined as:
+     * PublicKeyInfo ::= SEQUENCE {
+     *   algorithm       AlgorithmIdentifier,
+     *   PublicKey       BIT STRING
+     * }
+     * Where AlgorithmIdentifier is:
+     * AlgorithmIdentifier ::= SEQUENCE {
+     *   algorithm       OBJECT IDENTIFIER,     the OID of the enc algorithm
+     *   parameters      ANY DEFINED BY algorithm OPTIONAL (NULL for PKCS #1)
+     * }
+     * and PublicKey is a SEQUENCE encapsulated in a BIT STRING
+     * RSAPublicKey ::= SEQUENCE {
+     *   modulus           INTEGER,  -- n
+     *   publicExponent    INTEGER   -- e
+     * }
+     * it's possible to examine the structure of the keys obtained from openssl using
+     * an asn.1 dumper as the one used here to parse the components: http://lapo.it/asn1js/
+     * @argument {string} pem the pem encoded string, can include the BEGIN/END header/footer
+     * @private
+     */
+    JSEncryptRSAKey.prototype.parseKey = function (pem) {
+        try {
+            var modulus = 0;
+            var public_exponent = 0;
+            var reHex = /^\s*(?:[0-9A-Fa-f][0-9A-Fa-f]\s*)+$/;
+            var der = reHex.test(pem) ? Hex.decode(pem) : Base64.unarmor(pem);
+            var asn1 = ASN1.decode(der);
+            // Fixes a bug with OpenSSL 1.0+ private keys
+            if (asn1.sub.length === 3) {
+                asn1 = asn1.sub[2].sub[0];
+            }
+            if (asn1.sub.length === 9) {
+                // Parse the private key.
+                modulus = asn1.sub[1].getHexStringValue(); // bigint
+                this.n = parseBigInt(modulus, 16);
+                public_exponent = asn1.sub[2].getHexStringValue(); // int
+                this.e = parseInt(public_exponent, 16);
+                var private_exponent = asn1.sub[3].getHexStringValue(); // bigint
+                this.d = parseBigInt(private_exponent, 16);
+                var prime1 = asn1.sub[4].getHexStringValue(); // bigint
+                this.p = parseBigInt(prime1, 16);
+                var prime2 = asn1.sub[5].getHexStringValue(); // bigint
+                this.q = parseBigInt(prime2, 16);
+                var exponent1 = asn1.sub[6].getHexStringValue(); // bigint
+                this.dmp1 = parseBigInt(exponent1, 16);
+                var exponent2 = asn1.sub[7].getHexStringValue(); // bigint
+                this.dmq1 = parseBigInt(exponent2, 16);
+                var coefficient = asn1.sub[8].getHexStringValue(); // bigint
+                this.coeff = parseBigInt(coefficient, 16);
+            }
+            else if (asn1.sub.length === 2) {
+                // Parse the public key.
+                var bit_string = asn1.sub[1];
+                var sequence = bit_string.sub[0];
+                modulus = sequence.sub[0].getHexStringValue();
+                this.n = parseBigInt(modulus, 16);
+                public_exponent = sequence.sub[1].getHexStringValue();
+                this.e = parseInt(public_exponent, 16);
+            }
+            else {
+                return false;
+            }
+            return true;
+        }
+        catch (ex) {
+            return false;
+        }
+    };
+    /**
+     * Translate rsa parameters in a hex encoded string representing the rsa key.
+     *
+     * The translation follow the ASN.1 notation :
+     * RSAPrivateKey ::= SEQUENCE {
+     *   version           Version,
+     *   modulus           INTEGER,  -- n
+     *   publicExponent    INTEGER,  -- e
+     *   privateExponent   INTEGER,  -- d
+     *   prime1            INTEGER,  -- p
+     *   prime2            INTEGER,  -- q
+     *   exponent1         INTEGER,  -- d mod (p1)
+     *   exponent2         INTEGER,  -- d mod (q-1)
+     *   coefficient       INTEGER,  -- (inverse of q) mod p
+     * }
+     * @returns {string}  DER Encoded String representing the rsa private key
+     * @private
+     */
+    JSEncryptRSAKey.prototype.getPrivateBaseKey = function () {
+        var options = {
+            array: [
+                new KJUR.asn1.DERInteger({ int: 0 }),
+                new KJUR.asn1.DERInteger({ bigint: this.n }),
+                new KJUR.asn1.DERInteger({ int: this.e }),
+                new KJUR.asn1.DERInteger({ bigint: this.d }),
+                new KJUR.asn1.DERInteger({ bigint: this.p }),
+                new KJUR.asn1.DERInteger({ bigint: this.q }),
+                new KJUR.asn1.DERInteger({ bigint: this.dmp1 }),
+                new KJUR.asn1.DERInteger({ bigint: this.dmq1 }),
+                new KJUR.asn1.DERInteger({ bigint: this.coeff })
+            ]
+        };
+        var seq = new KJUR.asn1.DERSequence(options);
+        return seq.getEncodedHex();
+    };
+    /**
+     * base64 (pem) encoded version of the DER encoded representation
+     * @returns {string} pem encoded representation without header and footer
+     * @public
+     */
+    JSEncryptRSAKey.prototype.getPrivateBaseKeyB64 = function () {
+        return hex2b64(this.getPrivateBaseKey());
+    };
+    /**
+     * Translate rsa parameters in a hex encoded string representing the rsa public key.
+     * The representation follow the ASN.1 notation :
+     * PublicKeyInfo ::= SEQUENCE {
+     *   algorithm       AlgorithmIdentifier,
+     *   PublicKey       BIT STRING
+     * }
+     * Where AlgorithmIdentifier is:
+     * AlgorithmIdentifier ::= SEQUENCE {
+     *   algorithm       OBJECT IDENTIFIER,     the OID of the enc algorithm
+     *   parameters      ANY DEFINED BY algorithm OPTIONAL (NULL for PKCS #1)
+     * }
+     * and PublicKey is a SEQUENCE encapsulated in a BIT STRING
+     * RSAPublicKey ::= SEQUENCE {
+     *   modulus           INTEGER,  -- n
+     *   publicExponent    INTEGER   -- e
+     * }
+     * @returns {string} DER Encoded String representing the rsa public key
+     * @private
+     */
+    JSEncryptRSAKey.prototype.getPublicBaseKey = function () {
+        var first_sequence = new KJUR.asn1.DERSequence({
+            array: [
+                new KJUR.asn1.DERObjectIdentifier({ oid: "1.2.840.113549.1.1.1" }),
+                new KJUR.asn1.DERNull()
+            ]
+        });
+        var second_sequence = new KJUR.asn1.DERSequence({
+            array: [
+                new KJUR.asn1.DERInteger({ bigint: this.n }),
+                new KJUR.asn1.DERInteger({ int: this.e })
+            ]
+        });
+        var bit_string = new KJUR.asn1.DERBitString({
+            hex: "00" + second_sequence.getEncodedHex()
+        });
+        var seq = new KJUR.asn1.DERSequence({
+            array: [
+                first_sequence,
+                bit_string
+            ]
+        });
+        return seq.getEncodedHex();
+    };
+    /**
+     * base64 (pem) encoded version of the DER encoded representation
+     * @returns {string} pem encoded representation without header and footer
+     * @public
+     */
+    JSEncryptRSAKey.prototype.getPublicBaseKeyB64 = function () {
+        return hex2b64(this.getPublicBaseKey());
+    };
+    /**
+     * wrap the string in block of width chars. The default value for rsa keys is 64
+     * characters.
+     * @param {string} str the pem encoded string without header and footer
+     * @param {Number} [width=64] - the length the string has to be wrapped at
+     * @returns {string}
+     * @private
+     */
+    JSEncryptRSAKey.wordwrap = function (str, width) {
+        width = width || 64;
+        if (!str) {
+            return str;
+        }
+        var regex = "(.{1," + width + "})( +|$\n?)|(.{1," + width + "})";
+        return str.match(RegExp(regex, "g")).join("\n");
+    };
+    /**
+     * Retrieve the pem encoded private key
+     * @returns {string} the pem encoded private key with header/footer
+     * @public
+     */
+    JSEncryptRSAKey.prototype.getPrivateKey = function () {
+        var key = "-----BEGIN RSA PRIVATE KEY-----\n";
+        key += JSEncryptRSAKey.wordwrap(this.getPrivateBaseKeyB64()) + "\n";
+        key += "-----END RSA PRIVATE KEY-----";
+        return key;
+    };
+    /**
+     * Retrieve the pem encoded public key
+     * @returns {string} the pem encoded public key with header/footer
+     * @public
+     */
+    JSEncryptRSAKey.prototype.getPublicKey = function () {
+        var key = "-----BEGIN PUBLIC KEY-----\n";
+        key += JSEncryptRSAKey.wordwrap(this.getPublicBaseKeyB64()) + "\n";
+        key += "-----END PUBLIC KEY-----";
+        return key;
+    };
+    /**
+     * Check if the object contains the necessary parameters to populate the rsa modulus
+     * and public exponent parameters.
+     * @param {Object} [obj={}] - An object that may contain the two public key
+     * parameters
+     * @returns {boolean} true if the object contains both the modulus and the public exponent
+     * properties (n and e)
+     * @todo check for types of n and e. N should be a parseable bigInt object, E should
+     * be a parseable integer number
+     * @private
+     */
+    JSEncryptRSAKey.hasPublicKeyProperty = function (obj) {
+        obj = obj || {};
+        return (obj.hasOwnProperty("n") &&
+            obj.hasOwnProperty("e"));
+    };
+    /**
+     * Check if the object contains ALL the parameters of an RSA key.
+     * @param {Object} [obj={}] - An object that may contain nine rsa key
+     * parameters
+     * @returns {boolean} true if the object contains all the parameters needed
+     * @todo check for types of the parameters all the parameters but the public exponent
+     * should be parseable bigint objects, the public exponent should be a parseable integer number
+     * @private
+     */
+    JSEncryptRSAKey.hasPrivateKeyProperty = function (obj) {
+        obj = obj || {};
+        return (obj.hasOwnProperty("n") &&
+            obj.hasOwnProperty("e") &&
+            obj.hasOwnProperty("d") &&
+            obj.hasOwnProperty("p") &&
+            obj.hasOwnProperty("q") &&
+            obj.hasOwnProperty("dmp1") &&
+            obj.hasOwnProperty("dmq1") &&
+            obj.hasOwnProperty("coeff"));
+    };
+    /**
+     * Parse the properties of obj in the current rsa object. Obj should AT LEAST
+     * include the modulus and public exponent (n, e) parameters.
+     * @param {Object} obj - the object containing rsa parameters
+     * @private
+     */
+    JSEncryptRSAKey.prototype.parsePropertiesFrom = function (obj) {
+        this.n = obj.n;
+        this.e = obj.e;
+        if (obj.hasOwnProperty("d")) {
+            this.d = obj.d;
+            this.p = obj.p;
+            this.q = obj.q;
+            this.dmp1 = obj.dmp1;
+            this.dmq1 = obj.dmq1;
+            this.coeff = obj.coeff;
+        }
+    };
+    return JSEncryptRSAKey;
+}(RSAKey));
+
+/**
+ *
+ * @param {Object} [options = {}] - An object to customize JSEncrypt behaviour
+ * possible parameters are:
+ * - default_key_size        {number}  default: 1024 the key size in bit
+ * - default_public_exponent {string}  default: '010001' the hexadecimal representation of the public exponent
+ * - log                     {boolean} default: false whether log warn/error or not
+ * @constructor
+ */
+var JSEncrypt = /** @class */ (function () {
+    function JSEncrypt(options) {
+        options = options || {};
+        this.default_key_size = parseInt(options.default_key_size, 10) || 1024;
+        this.default_public_exponent = options.default_public_exponent || "010001"; // 65537 default openssl public exponent for rsa key type
+        this.log = options.log || false;
+        // The private and public key.
+        this.key = null;
+    }
+    /**
+     * Method to set the rsa key parameter (one method is enough to set both the public
+     * and the private key, since the private key contains the public key paramenters)
+     * Log a warning if logs are enabled
+     * @param {Object|string} key the pem encoded string or an object (with or without header/footer)
+     * @public
+     */
+    JSEncrypt.prototype.setKey = function (key) {
+        if (this.log && this.key) {
+            console.warn("A key was already set, overriding existing.");
+        }
+        this.key = new JSEncryptRSAKey(key);
+    };
+    /**
+     * Proxy method for setKey, for api compatibility
+     * @see setKey
+     * @public
+     */
+    JSEncrypt.prototype.setPrivateKey = function (privkey) {
+        // Create the key.
+        this.setKey(privkey);
+    };
+    /**
+     * Proxy method for setKey, for api compatibility
+     * @see setKey
+     * @public
+     */
+    JSEncrypt.prototype.setPublicKey = function (pubkey) {
+        // Sets the public key.
+        this.setKey(pubkey);
+    };
+    /**
+     * Proxy method for RSAKey object's decrypt, decrypt the string using the private
+     * components of the rsa key object. Note that if the object was not set will be created
+     * on the fly (by the getKey method) using the parameters passed in the JSEncrypt constructor
+     * @param {string} str base64 encoded crypted string to decrypt
+     * @return {string} the decrypted string
+     * @public
+     */
+    JSEncrypt.prototype.decrypt = function (str) {
+        // Return the decrypted string.
+        try {
+            return this.getKey().decrypt(b64tohex(str));
+        }
+        catch (ex) {
+            return false;
+        }
+    };
+    /**
+     * Proxy method for RSAKey object's encrypt, encrypt the string using the public
+     * components of the rsa key object. Note that if the object was not set will be created
+     * on the fly (by the getKey method) using the parameters passed in the JSEncrypt constructor
+     * @param {string} str the string to encrypt
+     * @return {string} the encrypted string encoded in base64
+     * @public
+     */
+    JSEncrypt.prototype.encrypt = function (str) {
+        // Return the encrypted string.
+        try {
+            return hex2b64(this.getKey().encrypt(str));
+        }
+        catch (ex) {
+            return false;
+        }
+    };
+    /**
+     * Proxy method for RSAKey object's sign.
+     * @param {string} str the string to sign
+     * @param {function} digestMethod hash method
+     * @param {string} digestName the name of the hash algorithm
+     * @return {string} the signature encoded in base64
+     * @public
+     */
+    JSEncrypt.prototype.sign = function (str, digestMethod, digestName) {
+        // return the RSA signature of 'str' in 'hex' format.
+        try {
+            return hex2b64(this.getKey().sign(str, digestMethod, digestName));
+        }
+        catch (ex) {
+            return false;
+        }
+    };
+    /**
+     * Proxy method for RSAKey object's verify.
+     * @param {string} str the string to verify
+     * @param {string} signature the signature encoded in base64 to compare the string to
+     * @param {function} digestMethod hash method
+     * @return {boolean} whether the data and signature match
+     * @public
+     */
+    JSEncrypt.prototype.verify = function (str, signature, digestMethod) {
+        // Return the decrypted 'digest' of the signature.
+        try {
+            return this.getKey().verify(str, b64tohex(signature), digestMethod);
+        }
+        catch (ex) {
+            return false;
+        }
+    };
+    /**
+     * Getter for the current JSEncryptRSAKey object. If it doesn't exists a new object
+     * will be created and returned
+     * @param {callback} [cb] the callback to be called if we want the key to be generated
+     * in an async fashion
+     * @returns {JSEncryptRSAKey} the JSEncryptRSAKey object
+     * @public
+     */
+    JSEncrypt.prototype.getKey = function (cb) {
+        // Only create new if it does not exist.
+        if (!this.key) {
+            // Get a new private key.
+            this.key = new JSEncryptRSAKey();
+            if (cb && {}.toString.call(cb) === "[object Function]") {
+                this.key.generateAsync(this.default_key_size, this.default_public_exponent, cb);
+                return;
+            }
+            // Generate the key.
+            this.key.generate(this.default_key_size, this.default_public_exponent);
+        }
+        return this.key;
+    };
+    /**
+     * Returns the pem encoded representation of the private key
+     * If the key doesn't exists a new key will be created
+     * @returns {string} pem encoded representation of the private key WITH header and footer
+     * @public
+     */
+    JSEncrypt.prototype.getPrivateKey = function () {
+        // Return the private representation of this key.
+        return this.getKey().getPrivateKey();
+    };
+    /**
+     * Returns the pem encoded representation of the private key
+     * If the key doesn't exists a new key will be created
+     * @returns {string} pem encoded representation of the private key WITHOUT header and footer
+     * @public
+     */
+    JSEncrypt.prototype.getPrivateKeyB64 = function () {
+        // Return the private representation of this key.
+        return this.getKey().getPrivateBaseKeyB64();
+    };
+    /**
+     * Returns the pem encoded representation of the public key
+     * If the key doesn't exists a new key will be created
+     * @returns {string} pem encoded representation of the public key WITH header and footer
+     * @public
+     */
+    JSEncrypt.prototype.getPublicKey = function () {
+        // Return the private representation of this key.
+        return this.getKey().getPublicKey();
+    };
+    /**
+     * Returns the pem encoded representation of the public key
+     * If the key doesn't exists a new key will be created
+     * @returns {string} pem encoded representation of the public key WITHOUT header and footer
+     * @public
+     */
+    JSEncrypt.prototype.getPublicKeyB64 = function () {
+        // Return the private representation of this key.
+        return this.getKey().getPublicBaseKeyB64();
+    };
+    JSEncrypt.version = "3.0.0-rc.1";
+    return JSEncrypt;
+}());
+
+window2.JSEncrypt = JSEncrypt;
+
+exports.JSEncrypt = JSEncrypt;
+exports.default = JSEncrypt;
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+})));

+ 34 - 0
js/lists.js

@@ -0,0 +1,34 @@
+const app = getApp();
+const appConfig = require("../config");
+const utils = require("../utils/util");
+const cache = require("cache");
+
+var lists = function(_this, page, pageSize) {
+  if (utils.getSwitchToLocalStatus()) {
+    var res = cache.getFromLocal(page, pageSize);
+    app.listSync(res);
+    return;
+  }
+  var header = {
+    'content-type': 'application/x-www-form-urlencoded',
+    'cookie': wx.getStorageSync(appConfig.storeKeys.sessionId)
+  };
+  wx.request({
+    url: utils.getUrl("lists", {
+      page: page,
+      pageSize: pageSize
+    }),
+    header: header,
+    dataType: "json",
+    success: function(res) {
+      app.listSync(res.data)
+    },
+    fail: function(res) {
+      app.listSync(appConfig.networkError);
+    }
+  });
+}
+
+module.exports = {
+  pwd: lists
+}

+ 157 - 0
js/login.js

@@ -0,0 +1,157 @@
+const app = getApp();
+const appConfig = require("../config");
+const utils = require("../utils/util");
+
+var login = function (that) {
+  var serverInfo = wx.getStorageSync(appConfig.storeKeys.serverInfo);
+  if(serverInfo && typeof serverInfo == 'object'
+    && serverInfo['openId'] && serverInfo['openId'] != "" 
+    && serverInfo['sessionId'] && serverInfo['sessionId'] != "") {
+    //如果是切换到本地就不去发请求,直接成功
+    if (utils.getSwitchToLocalStatus) {
+      that.setData({
+        motto: "登录成功",
+        showLists: true,
+        hasServerInfo: true,
+        hiddenStatus: true
+      });
+      app.serInfoReady({
+        code: 0,
+        msg: "本地登录成功~",
+        data: {
+          sslKeys: {}
+        }
+      })
+      return;
+    }
+
+    var header = {
+      'content-type': 'application/x-www-form-urlencoded',
+    };
+      wx.request({
+        url: utils.getUrl("login"),
+        header: header,
+        data : {
+          openId: serverInfo['openId'],
+          sessionId: serverInfo['sessionId'],
+        },
+        method : "POST",
+        dataType : "json",
+        success: function(req) {
+          //解密用户信息后登录
+          if (!req || req.data.code > 0 || !req.data.data || !req.data.data.sslKeys) {
+            //登录失败
+            app.serInfoReady(req.data);
+            setTimeout(function () {
+              wx.hideLoading();
+            }, 3000);
+            return;
+          } else {
+            //登录成功,将用户信息加密保存到本地
+            wx.showToast({
+              title: '登录成功',
+            })
+          }
+          wx.setStorageSync(appConfig.storeKeys.sessionId, req.header["Set-Cookie"])
+          wx.setStorageSync(appConfig.storeKeys.serverInfo, req.data.data)
+
+          that.setData({
+            motto: "登录成功",
+            showLists: true,
+            hasServerInfo: true,
+            hiddenStatus: true
+          });
+          if (req.data.data.sslKeys && req.data.data.sslKeys.publicKey && req.data.data.sslKeys.publicKey != "") {
+            that.setData({
+              hasPublicKey: true
+            });
+            app.globalData.hasPublicKey = true;
+          }
+          wx.hideToast();
+          wx.hideLoading();
+          app.serInfoReady(req.data)
+        },
+        fail: function (res) {
+          app.serInfoReady(appConfig.networkError);
+        }
+    });
+    return;
+  }
+  //如果有openId 和 sessionId 就直接登录,不走获取微信信息
+  wx.getSetting({
+    success(res) {
+      app.log("wx.getSetting", res);
+      if (res.authSetting['scope.userInfo']) {
+        if (!app.globalData.res) {
+          app.warning("用户信息获取失败");
+          return;
+        }
+        if (app.globalData.code == null) {
+          app.warning("登录授权码获取失败~");
+          return;
+        }
+        wx.showLoading({
+          title: '正在登录',
+        });
+
+        var header = {
+          'content-type': 'application/x-www-form-urlencoded',
+        };
+
+        wx.request({
+          url: utils.getUrl("auth"),
+          data: {
+            code: app.globalData.code,
+            encryptedData: app.globalData.res.encryptedData,
+            iv: app.globalData.res.iv
+          },
+          header: header,
+          method: 'POST',
+          dataType: 'json',
+          success: function (req) {
+            //解密用户信息后登录
+            if (!req || req.data.code > 0 || !req.data.data || !req.data.data.sslKeys) {
+              //登录失败
+              app.serInfoReady(req.data);
+              setTimeout(function () {
+                wx.hideLoading();
+              }, 3000);
+              return;
+            } else {
+              //登录成功,将用户信息加密保存到本地
+              wx.showToast({
+                title: '登录成功',
+              })
+            }
+            wx.setStorageSync(appConfig.storeKeys.sessionId, req.header["Set-Cookie"])
+            wx.setStorageSync(appConfig.storeKeys.serverInfo, req.data.data)
+
+            that.setData({
+              motto: "登录成功",
+              showLists: true,
+              hasServerInfo: true,
+              hiddenStatus: true
+            });
+            if (req.data.data.sslKeys && req.data.data.sslKeys.publicKey && req.data.data.sslKeys.publicKey != "") {
+              that.setData({
+                hasPublicKey: true
+              });
+              app.globalData.hasPublicKey = true;
+            }
+            wx.hideToast();
+            wx.hideLoading();
+            app.serInfoReady(req.data)
+          },
+          fail: function (res) {
+            app.serInfoReady(appConfig.networkError);
+          }
+        });
+      }else{
+        app.warning("请先授权此应用访问您的基本信息");
+      }
+    }
+  });
+  
+}
+
+module.exports = login

+ 95 - 0
js/rand.js

@@ -0,0 +1,95 @@
+var rand = {
+  alpha : "0123456789",
+  smallLetter : "abcdefghijklmnopqrstuvwxyz",
+  bigLetter : "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+  specialChars : ",.;:~!@#$%^&*-_",
+  shuffle : function (arr) {
+      var len = arr.length;
+      var randindex, tmp;
+      for (var i = len; i > 0; i--) {
+        randindex = Math.floor(Math.random() * i);//随机数的产生范围每次都变化
+        tmp = arr[randindex];
+        arr[randindex] = arr[i - 1];
+        arr[i - 1] = tmp;
+      }
+      return arr;
+  },
+  genRandStr : function (chars, length, unique) {
+    var str = [];
+    for (var i = 0; i < length; i++) {
+      if (chars.length == 0) break;
+      var index = this.rand(chars.length);
+      str.push(chars[index])
+      if (unique) {
+        chars.splice(index, 1)
+      }
+    }
+    return str;
+  },
+  getRndUniue : function (chars, length) {
+    var _chars = this.shuffle(chars);
+    return _chars.splice(0, length);
+  },
+  rand : function (max) {
+    return Math.floor(Math.random() * max);
+  },
+  genRandStr : function (chars, length, unique) {
+    var str = [];
+    for (var i = 0; i < length; i++) {
+      if (chars.length == 0) break;
+      var index = this.rand(chars.length);
+      str.push(chars[index])
+      if (unique) {
+        chars.splice(index, 1)
+      }
+    }
+    return str;
+  },
+  randAlpha : function (length, unique) {
+    var chars = this.alpha.split("");
+
+    return this.genRandStr(chars, length, unique);
+  },
+  randSmallLetter : function (length, unique) {
+    var chars = this.smallLetter.split("");
+
+    return this.genRandStr(chars, length, unique);
+  },
+  randBigLetter : function (length, unique) {
+    var chars = this.bigLetter.split("");
+
+    return this.genRandStr(chars, length, unique)
+  },
+  randCombin: function (combin, length, unique){
+    var chars = "";
+    var combin = combin.split(",");
+    for (var i in combin) {
+      var str = combin[i];
+      switch (str) {
+        case "alpha":
+          chars += this.alpha;
+          break;
+        case "smallLetter":
+          chars += this.smallLetter;
+          break;
+        case "bigLetter":
+          chars += this.bigLetter;
+          break;
+        case "specialChars":
+          chars += this.specialChars;
+          break;
+        default:
+          break;
+      }
+    }
+    //如果长度不够,就用当前串随机出不够长度的内容补齐
+    if (chars.length < length) {
+      var rndStr = this.shuffle(chars.split("")).join("");
+      chars += rndStr.substr(0, length - chars.length)
+    }
+    if (unique) return this.getRndUniue(chars.split(""), length).join("");
+    return this.genRandStr(chars, length, unique).join("");
+  },
+
+}
+module.exports = rand

+ 37 - 0
js/remove.js

@@ -0,0 +1,37 @@
+const app = getApp();
+const appConfig = require("../config");
+const utils = require("../utils/util");
+const cache = require("cache");
+
+var remove = function (id, name) {
+  //切换到本地后,从本地删除
+  if (utils.getSwitchToLocalStatus()) {
+    cache.removeItemByName(id, name);
+    app.removeSuccessSync({
+      code : 0,
+      msg : "删除成功",
+    })
+    return;
+  }
+  var header = {
+    'content-type': 'application/x-www-form-urlencoded',
+    'cookie': wx.getStorageSync(appConfig.storeKeys.sessionId)
+  };
+  wx.request({
+    url: utils.getUrl("remove"),
+    header: header,
+    data : {
+      ids : id
+    },
+    method: "POST",
+    dataType: "json",
+    success: function (req) {
+      app.removeSuccessSync(req.data)
+    },
+    fail: function (req) {
+      app.removeSuccessSync(appConfig.networkError)
+    }
+  })
+}
+
+module.exports = remove

+ 32 - 0
js/resetKeys.js

@@ -0,0 +1,32 @@
+const app = getApp();
+const appConfig = require("../config");
+const utils = require("../utils/util");
+
+var saveKeys = function (_this, forceUpdate) {
+  var header = {
+    'content-type': 'application/x-www-form-urlencoded',
+    'cookie': wx.getStorageSync(appConfig.storeKeys.sessionId)
+  };
+  var data = {
+    'public_key': _this.data.publicKey,
+    'private_key': _this.data.privateKey,
+  };
+  if (forceUpdate) {
+    data.forceUpdate = forceUpdate;
+  }
+  wx.request({
+    url: utils.getUrl("resetPublicKey", {opt : _this.data.opt}),
+    data: data,
+    header: header,
+    method: 'POST',
+    dataType: 'json',
+    success: function (res) {
+      app.sslKeySaveSync(res.data);
+    },
+    fail : function(res) {
+      app.sslKeySaveSync(appConfig.networkError);
+    }
+  })
+}
+
+module.exports = saveKeys

+ 35 - 0
js/saveClientPublicKey.js

@@ -0,0 +1,35 @@
+const app = getApp();
+const appConfig = require("../config");
+const utils = require("../utils/util");
+
+var saveClientPublicKey = function (_this, publicKey, privateKey) {
+  var header = {
+    'content-type': 'application/x-www-form-urlencoded',
+    'cookie': wx.getStorageSync(appConfig.storeKeys.sessionId)
+  };
+  var data = {
+    'publicKey': publicKey,
+  };
+  wx.request({
+    url: utils.getUrl("savePublicKeyClient"),
+    data: data,
+    header: header,
+    method: 'POST',
+    dataType: 'json',
+    success: function (res) {
+      if(res && res.data){
+        if(!res.data.data) {
+          res.data.data = {};
+        }
+        res.data.data.publicKey = publicKey;
+        res.data.data.privateKey = privateKey;
+      }
+      app.sslKeySaveSync(res.data);
+    },
+    fail: function (res) {
+      app.sslKeySaveSync(appConfig.networkError);
+    }
+  })
+}
+
+module.exports = saveClientPublicKey

+ 57 - 0
js/saveKeys.js

@@ -0,0 +1,57 @@
+const app = getApp();
+const appConfig = require("../config");
+const utils = require("../utils/util");
+
+var saveKeys = function (that, forceUpdate) {
+  var header = {
+    'content-type': 'application/x-www-form-urlencoded',
+    'cookie': wx.getStorageSync(appConfig.storeKeys.sessionId)
+  };
+  var data = {
+    'public_key': that.data.publicKey,
+    'private_key' : that.data.privateKey,
+  };
+  if (forceUpdate) {
+    data.forceUpdate = forceUpdate;
+  }
+  wx.request({
+    url: utils.getUrl('savePublicKey'),
+    data : data,
+    header: header,
+    method : 'POST',
+    dataType: 'json',
+    success: function (res) {
+      app.log(res)
+      if (res.data.code == 5) {
+        wx.showModal({
+          title: '提示',
+          content: res.data.msg || "保存失败",
+          cancelText: "取消",
+          confirmText: "更新",
+          success: function (item) {
+            if (item.confirm) {
+              app.log("更新", res.data.forceUpdate);
+              saveKeys(that, res.data.forceUpdate);
+            } else {
+            }
+          }
+        });
+      }else if(res.data.code > 0){
+        wx.showModal({
+          title: '提示',
+          content: res.data.msg || "保存失败",
+          showCancel: false,
+          success: function (res) {
+          }
+        });
+        return;
+      }
+      app.sslKeySaveSync(res.data);
+    },
+    fail : function(res) {
+      app.sslKeySaveSync(appConfig.networkError);
+    }
+  })
+}
+
+module.exports = saveKeys

+ 75 - 0
js/savePwd.js

@@ -0,0 +1,75 @@
+const app = getApp()
+const appConfig = require("../config");
+const valid = require("../js/valid");
+const utils = require("../utils/util");
+const cache = require("cache");
+
+var savePwd = function(that, data){
+  if(!data) {
+    wx.showToast({
+      title: '参数不正确,请检查后重试!~',
+      duration: appConfig.duration
+    });
+    return;
+  }
+  if (!valid.minlength(data.name, 2)) {
+    app.warning("名称不能少于2个字符");
+    return;
+  }
+  if (!valid.minlength(data.account, 2)) {
+    app.warning("账户名称不能少于3个字符");
+    return;
+  }
+  if (!valid.minlength(data.password, 6)) {
+    app.warning("密码不能少于6个字符");
+    return;
+  }
+  if(!valid.isHttp(data.link)) {
+    app.warning("链接地址不正确");
+    return;
+  }
+  var header = {
+    'content-type': 'application/x-www-form-urlencoded',
+    'cookie': wx.getStorageSync(appConfig.storeKeys.sessionId)
+  };
+  if (utils.getSwitchToLocalStatus()){
+    var timestamp = Date.parse(new Date());
+    data.password = utils.encrypt(data.password, data.pubKey);
+    delete data.pubKey;
+    data.create_at = data.update_at = timestamp.toString().substring(0, 10);
+    data.create_time = utils.showCreateTime();
+    if(data.isEdit) {
+      delete data.isEdit;
+      cache.editSingle(data);
+    }else{
+
+      cache.addItem(data);
+    }
+
+    app.storeSuccess({
+      code : 0, 
+      msg : "保存成功"
+    });
+    return;
+  }
+  wx.request({
+    url: utils.getUrl('savePwd'),
+    data : data,
+    header : header,
+    method : "POST",
+    dataType : "json",
+    success : function(res) {
+      app.log(res)
+      app.storeSuccess(res.data);
+    },
+    fail : function(res) {
+      wx.showToast({
+        title: '保存失败,请重试',
+        duration: appConfig.duration
+      });
+      app.storeSuccess(appConfig.networkError);
+    }
+  })
+}
+
+module.exports = savePwd

+ 32 - 0
js/valid.js

@@ -0,0 +1,32 @@
+var valid = {
+  isValid : function(reg, str) {
+    if (str == "" || !reg.test(str)) {
+      return false;
+    }
+    return true;
+  },
+  isEmail : function (str) {
+    var reg = /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/;
+    return this.isValid(reg, str);
+  },
+  isChinese : function (str) {
+    var reg = /^[\u0391-\uFFE5]+$/;
+    return this.isValid(reg, str);
+  },
+  isEnglish : function (str) {
+    var reg = /^[a-zA-Z]*$/;
+    return this.isValid(reg, str);
+  },
+  isNumber : function (str) {
+    var reg = /^[0-9]+$/;
+    return this.isValid(reg, str);
+  },
+  isHttp: function (str) {
+    var reg = /[a-zA-Z]+:\/\/[^\s]*/;
+    return this.isValid(reg, str);
+  },
+  minlength : function(str, len){
+    return str.length >= len
+  }
+}
+module.exports = valid

+ 28 - 0
js/verifyKeys.js

@@ -0,0 +1,28 @@
+const app = getApp()
+const appConfig = require("../config");
+const utils = require("../utils/util");
+
+var verifyKeys = function (publicKey, privateKey) {
+  var header = {
+    'content-type': 'application/x-www-form-urlencoded',
+    'cookie': wx.getStorageSync(appConfig.storeKeys.sessionId)
+  };
+  wx.request({
+    url: utils.getUrl('verifyKeys'),
+    header: header,
+    data : {
+      public_key : publicKey,
+      private_key : privateKey
+    },
+    method: "POST",
+    dataType: "json",
+    success: function (req) {
+      app.verifyKeysSuccessSync(req.data)
+    },
+    fail: function (req) {
+      app.verifyKeysSuccessSync(appConfig.networkError);
+    }
+  })
+}
+
+module.exports = verifyKeys

+ 83 - 0
pages/components/about/about.js

@@ -0,0 +1,83 @@
+const app = getApp()
+const cache = require('../../../js/cache')
+const appConfig = require('../../../config')
+const utils = require('../../../utils/util')
+const common = require('../../../common')
+
+Component({
+  properties: {
+    switchToLocalText: {
+      type: String,
+      value: utils.getSwitchToLocalStatus() ? "切换到线上使用" : "切换到本地使用"
+    }
+  },
+  options: {
+    addGlobalClass: true,
+  },
+  data: {
+    starCount: 0,
+    forksCount: 0,
+    visitTotal: 0,
+    switchToLocalText: utils.getSwitchToLocalStatus() ? "切换到线上使用" : "切换到本地使用"
+  },
+  methods: {
+    clearData: function () {
+      try {
+        wx.clearStorageSync();
+        app.toast("清除成功");
+      } catch (e) {
+        app.toast("清空失败");
+      }
+    },
+    showQrcode() {
+      wx.previewImage({
+        urls: ['https://www.travelzs.com/static/images/donating-weixin.jpg'],
+        current: 'https://www.travelzs.com/static/images/donating-weixin.jpg' // 当前显示图片的http链接      
+      })
+    },
+    switchToLocal: function(){
+      var _this = this;
+      app.syncRes = res => {
+        if(res.code == 0) {
+          var val = 0;
+          if (!utils.getSwitchToLocalStatus()) {
+            val = 1;
+          }
+          app.toast("同步成功~");
+          wx.setStorageSync(appConfig.storeKeys.switchToLocal, val);
+          _this.setData({
+            "switchToLocalText": utils.getSwitchToLocalStatus() ? "切换到线上使用" : "切换到本地使用"
+          });
+        }else{
+          app.warning("同步失败~");
+        }
+      }
+      //如果用户未登录,要求用户先登录
+      if (!utils.hasLogined() && !utils.getSwitchToLocalStatus()){
+        app.warning("请登录后再切换,否则无法同步数据~");
+        return;
+      }
+      if(utils.getSwitchToLocalStatus()) {
+        wx.showModal({
+          title: '温馨提示',
+          content: '切换到线上后,数据将会在多台设备同步,点击确认将实时同步数据',
+          success: function (item) {
+            if (item.confirm) {
+              cache.sync();
+            }
+          }
+        })
+        return;
+      }
+      wx.showModal({
+        title: '温馨提示',
+        content: '切换到本地后,只能保存数据到本地,如果需要多台设备同步,请实时同步数据',
+        success: function(item){
+            if(item.confirm){
+              cache.sync();
+            }
+        }
+      })
+    },
+  }
+})

+ 4 - 0
pages/components/about/about.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 34 - 0
pages/components/about/about.wxml

@@ -0,0 +1,34 @@
+<scroll-view scroll-y class="scrollPage padding-tabbar">
+  <view class="UCenter-bg">
+    <image src="/images/logo.png" class="png" mode="widthFix"></image>
+    <view class="text-xl">密码本
+      <text class="text-df">v1.0</text>
+    </view>
+    <view class="margin-top-sm">
+      <text>By:朱金辉</text>
+    </view>
+    
+
+  <view class="align-center margin-top">
+    <button class="cu-btn bg-red round justify-middle" bindtap='showQrcode'>赞赏支持</button>
+  </view>
+  <view class="align-center margin-top">
+    <button class="cu-btn bg-yellow round justify-middle" bindtap='switchToLocal'>{{switchToLocalText}}</button>
+  </view>
+
+  <view class="padding flex flex-wrap justify-between align-center margin-top">
+    <button class="cu-btn bg-red round justify-middle" bindtap='clearData'>清空数据重新登录</button>
+  </view>
+
+  </view>
+  <view class="cu-tabbar-height" style="opacity:{{0.85}}">
+    <view class='margin-xl bg-white padding-xl radius shadow-lg'>
+      <view class='text-center margin-bottom text-lg  text-grey'>关于密码本</view>
+      <view class='text-content'>
+        <view>Hi!大家好~欢迎使用密码本!</view>
+        <view class='margin-top-sm'> 该项目的背景是由于不久前邮箱密码被盗,被小偷将我的一个短域名转走,从此认识到密码的重要性,由于平时的密码设置为了方便记忆会将密码设置得短,密码强度不高,很容易被破解,设置过于复杂又不方便记忆,所以开发密码本来管理自己的密码,您可以通过它生成复杂的随机密码,也可以保存您自定义的密码。</view>
+        <view class='margin-top-sm'>密码本的密码都是通过OpenSSL 256 位非对称加密,所以请您生成公私钥后,一定要保存好自己的私钥,防止私钥丢失导致密码泄露,如果您忘记了私钥,密码将会全部丢失。</view>
+      </view>
+    </view>
+  </view>
+</scroll-view>

+ 33 - 0
pages/components/about/about.wxss

@@ -0,0 +1,33 @@
+.UCenter-bg {
+  background-size: cover;
+  height: 550rpx;
+  display: flex;
+  justify-content: center;
+  padding-top: 40rpx;
+  overflow: hidden;
+  position: relative;
+  flex-direction: column;
+  align-items: center;
+  color: #fff;
+  font-weight: 300;
+  text-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
+}
+
+.UCenter-bg text {
+  opacity: 0.8;
+}
+
+.UCenter-bg image {
+  width: 200rpx;
+  height: 200rpx;
+}
+
+.UCenter-bg .gif-wave{
+  position: absolute;
+  width: 100%;
+  bottom: 0;
+  left: 0;
+  z-index: 99;
+  mix-blend-mode: screen;  
+  height: 100rpx;   
+}

+ 25 - 0
pages/components/background/background.js

@@ -0,0 +1,25 @@
+// pages/components/background.js
+const util = require("../../../utils/util");
+Component({
+  /**
+   * 组件的属性列表
+   */
+  properties: {
+     background:{
+       type: String,
+       value: util.getBackground()
+     }
+  },
+
+  /**
+   * 组件的初始数据
+   */
+  data: {
+  },
+
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+  }
+})

+ 4 - 0
pages/components/background/background.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 3 - 0
pages/components/background/background.wxml

@@ -0,0 +1,3 @@
+  <view class="bg" style="opacity:{{0.75}}">
+    <image class="bg-image" mode="aspectFill" src='{{background}}'></image>
+  </view>

+ 17 - 0
pages/components/background/background.wxss

@@ -0,0 +1,17 @@
+.bg {
+  position: fixed;
+  flex-direction: column;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 100vh;
+  background-repeat: no-repeat #000;
+  background-size: cover;
+  background-position: left;
+  z-index: -1;
+}
+
+.bg image{
+  width: 100%;
+  height: 100%;
+}

+ 631 - 0
pages/index/index.js

@@ -0,0 +1,631 @@
+//index.js
+//获取应用实例
+const app = getApp()
+const appConfig = require('../../config');
+const common = require('../../common');
+const utils = require('../../utils/util');
+const cache = require('../../js/cache');
+
+Page({
+  data: {
+    motto: '请点击上边图标以授权此应用',
+    userInfo: {},
+    hiddenStatus: false,
+    showLists: false,
+    hasUserInfo: false,
+    canIUse: wx.canIUse('button.open-type.getUserInfo'),
+    serverInfo: null,
+    hasServerInfo: false,
+    PageCur: 'passwd-note',
+    lists: null,
+    pages: null,
+    hasPublicKey: false,
+    iconCls: 3,
+    background: "",
+    switchToLocalText: "",
+    colors: ["bg-yellow", "bg-green", "bg-olive", "bg-green", "bg-cyan", "bg-blue", "bg-purple", "bg-mauve", "bg-pink", "bg-brown", "bg-grey", "bg-black"],
+    isLoad: false,
+    cardCur: 0,
+    DotStyle: "square-dot",
+    swiperList: [{
+      id: 0,
+      type: 'image',
+      url: '/images/theme/10001.jpg'
+    }, {
+      id: 1,
+      type: 'image',
+        url: '/images/theme/10002.jpg',
+    }, {
+      id: 2,
+      type: 'image',
+        url: '/images/theme/10003.jpg'
+    }, {
+      id: 3,
+      type: 'image',
+        url: '/images/theme/10004.jpg'
+    }, {
+      id: 4,
+      type: 'image',
+        url: '/images/theme/10005.jpg'
+    }, {
+      id: 5,
+      type: 'image',
+        url: '/images/theme/10006.jpg'
+    }, {
+      id: 6,
+      type: 'image',
+        url: '/images/theme/10007.jpg'
+    }],
+  },
+  NavChange(e) {
+    this.setData({
+      PageCur: e.currentTarget.dataset.cur,
+      switchToLocalText: utils.getSwitchToLocalStatus() ? "切换到线上使用" : "切换到本地使用"
+    })
+  },
+  showModal(e) {
+    this.setData({
+      modalName: e.currentTarget.dataset.target
+    })
+  },
+  hideModal(e) {
+    this.setData({
+      modalName: null
+    })
+  },
+  gridchange: function(e) {
+    this.setData({
+      gridCol: e.detail.value
+    });
+  },
+  gridswitch: function(e) {
+    this.setData({
+      gridBorder: e.detail.value
+    });
+  },
+  menuBorder: function(e) {
+    this.setData({
+      menuBorder: e.detail.value
+    });
+  },
+  menuArrow: function(e) {
+    this.setData({
+      menuArrow: e.detail.value
+    });
+  },
+  menuCard: function(e) {
+    this.setData({
+      menuCard: e.detail.value
+    });
+  },
+  switchSex: function(e) {
+    this.setData({
+      skin: e.detail.value
+    });
+  },
+
+  // ListTouch触摸开始
+  ListTouchStart(e) {
+    this.setData({
+      ListTouchStart: e.touches[0].pageX
+    })
+  },
+
+  // ListTouch计算方向
+  ListTouchMove(e) {
+    this.setData({
+      ListTouchDirection: e.touches[0].pageX - this.data.ListTouchStart > 0 ? 'right' : 'left'
+    })
+  },
+
+  // ListTouch计算滚动
+  ListTouchEnd(e) {
+    if (this.data.ListTouchDirection == 'left') {
+      this.setData({
+        modalName: e.currentTarget.dataset.target
+      })
+    } else {
+      this.setData({
+        modalName: null
+      })
+    }
+    this.setData({
+      ListTouchDirection: null
+    })
+  },
+  onPullDownRefresh:function(){
+    wx.showNavigationBarLoading();
+    common.lists(this, 1, appConfig.pageSize);
+    wx.stopPullDownRefresh();
+  },
+  onReachBottom: function(){
+    if(!this.data.pages) {
+        return;
+    }
+    if (this.data.pages.currentPage < this.data.pages.totalPage) {
+      this.setData({
+        hiddenLoading: false,
+      });
+      //加载下一页
+      common.lists(this, parseInt(this.data.pages.currentPage) + 1, appConfig.pageSize);
+    }
+  },
+  setToBackground: function(e){
+    var url = e.target.dataset.src;
+    this.setData({
+      "background" : url
+    });
+    app.globalData.background = url;
+    utils.setBackground(url);
+  },
+  //事件处理函数
+  bindViewTap: function() {
+    if (!utils.hasLogined()) {
+      app.log("bindViewTap reLogin");
+      this.reLogin();
+    }
+  },
+  clickItem: function (e) {
+    app.log("utils.hasLogined", utils.hasLogined());
+    var _this = this;
+    var method = e.target.dataset.id;
+    switch (method) {
+      case "add-password":
+        if (!utils.hasLogined()) {
+          wx.showModal({
+            title: '温馨提示',
+            content: '您尚未登录,请登录后再试~',
+            success: function(item) {
+                if(item.confirm) {
+                  _this.reLogin();
+                }
+            }
+          })
+          return;
+        }
+        if (!utils.hasPublicKey()) {
+          wx.navigateTo({
+            url: '../keys/keys?to=' + encodeURIComponent("../password/password"),
+          })
+          return;
+        }
+        wx.navigateTo({
+          url: '../password/password',
+        });
+        break;
+      case "request-ssl-key":
+        if (!utils.hasLogined()) {
+          wx.showModal({
+            title: '温馨提示',
+            content: '您尚未登录,请登录后再试~',
+            success: function (item) {
+              if (item.confirm) {
+                _this.reLogin();
+              }
+            }
+          })
+          return;
+        }
+        wx.navigateTo({
+          url: '../keys/keys',
+        });
+        break;
+      case "reset-ssl-key":
+        if (!utils.hasLogined()) {
+          wx.showModal({
+            title: '温馨提示',
+            content: '您尚未登录,请登录后再试~',
+            success: function (item) {
+              if (item.confirm) {
+                _this.reLogin();
+              }
+            }
+          })
+          return;
+        }
+        if (!utils.hasPublicKey()) {
+          wx.navigateTo({
+            url: '../keys/keys?to=' + encodeURIComponent("../keys/keys"),
+          })
+          return;
+        }
+        wx.navigateTo({
+          url: '../reset/reset',
+        });
+        break;
+      case 'save-to-local':
+        app.log('save-to-local');
+        cache.download(1, 10000);
+      break;
+    }
+  },
+  onShow: function() {
+    app.globalData.info = null;
+    if (app.globalData.isReloadLists) {
+      this.setData({
+        hiddenLoading: false,
+      });
+      common.lists(this, 1, appConfig.pageSize);
+      app.globalData.isReloadLists = false;
+    }
+    if (app.globalData.isReloadIcon) {
+      this.reloadIcon(false);
+      app.globalData.isReloadIcon = false;
+    }
+    this.setData({
+      "switchToLocalText": utils.getSwitchToLocalStatus() ? "切换到线上使用" : "切换到本地使用"
+    });
+
+  },
+  reLogin: function() {
+    app.login();
+  },
+  editPwd: function(e) {
+    var _this = this;
+    app.infoSuccessSync = res => {
+      app.log(res)
+      if (!res || (res.code && res.code > 0)) {
+        app.toast(res.msg || "获取失败");
+        return;
+      }
+      app.globalData.info = res.data;
+      wx.navigateTo({
+        url: '/pages/password/password?id=' + res.data.id,
+      })
+    }
+    if (!utils.syncServerInfo()) {
+      wx.showModal({
+        title: '温馨提示',
+        content: '您尚未登录,请登录后再试~',
+        success: function (item) {
+          if (item.confirm) {
+            _this.reLogin();
+          }
+        }
+      })
+      return;
+    }else if(!utils.hasPublicKey() && !utils.hasPrivateKey()){
+      wx.showModal({
+        title: '温馨提示',
+        content: '请先生成您的公私钥以便加解密密码',
+        showCancel: false,
+        success: function (item) {
+          wx.navigateTo({
+            url: '/pages/keys/keys',
+          })
+        }
+      })
+    } else if (!utils.hasPrivateKey()) {
+      wx.showModal({
+        title: '温馨提示',
+        content: '请输入您的私钥以解密您的密码',
+        showCancel: false,
+        success: function(item) {
+          wx.navigateTo({
+            url: '/pages/privatekey/privatekey',
+          })
+        }
+      })
+      return;
+    }
+    var id = e.target.dataset.id;
+    common.info(id);
+  },
+  removePwd: function(e) {
+    var id = e.target.dataset.id;
+    var name = e.target.dataset.name;
+    app.log(id, name)
+    app.removeSuccessSync = res => {
+      app.log("app.removeSuccessSync");
+      if (!res || (res.code && res.code > 0)) {
+        app.warning(res.msg || "删除失败");
+        return;
+      }
+      wx.showModal({
+        title: '温馨提示',
+        content: '删除成功',
+        showCancel: false,
+        success: function(item) {}
+      });
+      for (var i in this.data.lists) {
+        var list = this.data.lists[i];
+        if (list.id == id) {
+          this.data.lists.splice(i, 1);
+        }
+      }
+      this.setData({
+        lists: this.data.lists
+      })
+    }
+    wx.showModal({
+      title: '温馨提示',
+      content: '数据一旦删除无法恢复,请确认是否要删除~',
+      success: function(item) {
+        if (item.confirm) {
+          common.remove(id, name)
+        }
+      }
+    });
+  },
+  copyPwd: function(e) {
+    var _this = this;
+    if(!utils.hasLogined()){
+      wx.showModal({
+        title: '温馨提示',
+        content: '您尚未登录,请登录后再试~',
+        success: function (item) {
+          if (item.confirm) {
+            _this.reLogin();
+          }
+        }
+      })
+      return;
+    }
+    if (!utils.hasPrivateKey()) {
+      wx.showModal({
+        title: '温馨提示',
+        content: '请输入您的私钥以解密您的密码',
+        success: function(item) {
+          if(item.confirm) {
+            wx.navigateTo({
+              url: '/pages/privatekey/privatekey',
+            })
+          }
+        }
+      })
+      return;
+    }
+    var str = e.target.dataset.val;
+    if (!str || str == '') {
+      app.warning("解密的密码不能为空~");
+      return;
+    }
+
+    app.sslKeyDecryptSync = res => {
+      app.log(res);
+      if (!res ||  (res.code && res.code > 0)) {
+        app.warning(res.msg || "解密失败");
+        return;
+      }
+      var password = res.data.password;
+      wx.showModal({
+        title: '解密成功',
+        content: '您的密码是:' + password,
+        confirmText: '复制',
+        success: function(item) {
+          if (item.confirm) {
+            wx.setClipboardData({
+              data: password,
+              success: function(res) {
+                wx.getClipboardData({
+                  success: function(res) {
+                  }
+                });
+              },
+              fail: function(){
+                app.toast("复制失败");
+              }
+            });
+          }
+        }
+      })
+
+    }
+    var res = { "code": 0, "msg": "解密成功" };
+    try{
+      var decrypt = utils.sslDecrypt(str, utils.getPrivateKey())
+      if (decrypt == false) {
+        res = { "code": 1, "msg": "解密失败,请检查您的私钥及密码串~" };
+      } else {
+        res.data = { password: decrypt };
+      }
+    }catch(e){
+      res.code = 400;
+      res.msg = "解密失败,请检查您的私钥及密码串~";
+    }
+    app.sslKeyDecryptSync(res);
+    //去掉网络请求
+    //utils.decrypt(str, utils.getPrivateKey());
+  },
+  reloadIcon: function(loadList) {
+    var _this = this;
+    if (!utils.syncServerInfo()) {
+      wx.showModal({
+        title: '温馨提示',
+        content: '您尚未登录,请登录后再试~',
+        success: function (item) {
+          if (item.confirm) {
+            _this.reLogin();
+          }
+        }
+      })
+      return;
+    } else if (!utils.hasPublicKey() && !utils.hasPrivateKey()){
+      wx.showModal({
+        title: '温馨提示',
+        content: '请先生成您的公私钥以便加解密密码',
+        success: function (item) {
+          if (item.confirm) {
+            wx.navigateTo({
+              url: '/pages/keys/keys',
+            })
+          }
+        }
+      });
+    } /*else if (!utils.hasPrivateKey()) {
+      wx.showModal({
+        title: '温馨提示',
+        content: '请输入您的私钥以解密您的密码',
+        success: function(item) {
+          app.log(item);
+          if (item.confirm) {
+            wx.navigateTo({
+              url: '/pages/privatekey/privatekey',
+            })
+          }
+        }
+      });
+    }*/
+    if (utils.hasPublicKey()) {
+      this.setData({
+        'iconCls': appConfig.noPublicKeyIcon.length,
+        'iconList': appConfig.noPublicKeyIcon
+      });
+    }
+    if (utils.syncServerInfo() && loadList) {
+      this.setData({
+        hiddenLoading: false,
+      });
+      common.lists(this, 1, appConfig.pageSize)
+    }
+  },
+  onLoad: function() {
+    var _this = this;
+
+    app.syncRes = res => {
+      if (res.code == 0) {
+        app.toast("同步成功~");
+      }else{
+        app.warning("同步失败~");
+      }
+    }
+    app.listSync = res => {
+      if(!res || !res.pages) {
+        app.warning(res.msg || "获取数据失败");
+        if (res.code == '40001') {
+          _this.reLogin();
+        }
+        return;
+      }
+      this.setData({
+        hiddenLoading : true,
+      });
+      setTimeout(function () {
+        wx.hideNavigationBarLoading();
+      }, 1000);
+      if (res.pages.currentPage == res.pages.totalPage) {
+        this.setData({
+          "isLoad" : true
+        });
+        this.setData({
+          hiddenLoading: false,
+        });
+      }
+      var lists = [];
+      if(res.pages.currentPage == 1) {
+        lists = res.lists;
+      }else{
+        lists = this.data.lists;
+        for (var i in res.lists) {
+          lists.push(res.lists[i]);
+        }
+      }
+      this.setData({
+        lists: lists,
+        pages: res.pages
+      });
+    }
+    //同步数据回调
+    app.syncSuccess = res => {
+      if(!res || res.code > 0) {
+        app.warning(res.msg || "同步失败");
+        return;
+      }
+
+      app.toast("同步成功");
+      if (utils.getSwitchToLocalStatus) {
+        common.lists(this, 1, appConfig.pageSize);
+      }
+    }
+    //保存数据到本地回调
+    app.storeToLocal = res => {
+      app.log(res);
+    }
+
+    app.serInfoReady = res => {
+      var _this = this;
+      app.log("app.serInfoReady(serverInfo)", res);
+      app.log("app.serInfoReady(serverInfo).code", res.code);
+      if (!res || res.code > 0 || !res.data || !res.data.sslKeys) {
+        if (res.code == 10005) {
+          //删除本地登录信息,重新登录
+          wx.clearStorageSync();
+        }
+        wx.showModal({
+          title: '温馨提示',
+          content: res.msg || "登录失败,请重试 ~",
+          showCancel: false,
+          success: function(item) {
+            app.log(item);
+            _this.reLogin();
+          }
+        })
+        return;
+      }
+      this.reloadIcon(true);
+    }
+
+    app.globalData.serverInfo = utils.syncServerInfo();
+    app.globalData.hasPublicKey = utils.hasPublicKey();
+    if (utils.hasPublicKey()) {
+      this.setData({
+        'iconCls': appConfig.noPublicKeyIcon.length,
+        'iconList': appConfig.noPublicKeyIcon
+      });
+    } else {
+      this.setData({
+        'iconCls': appConfig.iconList.length,
+        "iconList": appConfig.iconList
+      });
+    }
+    if (app.globalData.userInfo) {
+      this.setData({
+        userInfo: app.globalData.userInfo,
+        hasUserInfo: true
+      });
+      common.login(this);
+    } else if (this.data.canIUse) {
+      // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
+      // 所以此处加入 callback 以防止这种情况
+      app.userInfoReadyCallback = res => {
+        app.log("app.userInfoReadyCallback");
+        app.globalData.res = res
+        common.login(this)
+        app.log(1, res.userInfo)
+        this.setData({
+          motto: "正在登录,请稍后...",
+          userInfo: res.userInfo,
+          hasUserInfo: true,
+        })
+      }
+    } else {
+      this.reLogin();
+    }
+
+    this.setData({
+      background: utils.getBackground()
+    });
+  },
+  getUserInfo: function(e) {
+    if(!e.detail.userInfo){
+      app.warning("获取用户信息失败,请先授权此应用访问您的基本信息~");
+      return;
+    }
+    /*
+    app.globalData.userInfo = e.detail.userInfo;
+    app.globalData.res = e.detail;
+    this.setData({
+      userInfo: e.detail.userInfo,
+      hasUserInfo: true
+    })
+    common.login(this)*/
+    app.login();
+  },
+  onShareAppMessage() {
+    return {
+      title: 'mobi密码本,您的密码守护专家',
+      imageUrl: '/images/share.png',
+      path: '/pages/index/index'
+    }
+  },
+})

+ 6 - 0
pages/index/index.json

@@ -0,0 +1,6 @@
+{
+  "usingComponents": {
+    "about": "/pages/components/about/about",
+    "background": "/pages/components/background/background"
+  }
+}

+ 84 - 0
pages/index/index.wxml

@@ -0,0 +1,84 @@
+<background background="{{background}}"></background>
+<about wx:if="{{PageCur=='about'}}" switchToLocalText="{{switchToLocalText}}"></about>
+<home wx:if="{{PageCur=='passwd-note'}}">
+  <view class="container">
+    <view class="userinfo">
+      <button class="userinfo-avatar-unauth lg font-24 no-border" plain="true" wx:if="{{!hasUserInfo && canIUse}}" open-type="getUserInfo" bindgetuserinfo="getUserInfo">
+        <image src="/images/unlock.png"></image>
+      </button>
+      <block wx:else>
+        <image bindtap="bindViewTap" class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover"></image>
+        <text class="userinfo-nickname white">{{userInfo.nickName}}</text>
+      </block>
+    </view>
+    <scroll-view scroll-y="{{modalName==null}}" class="page {{modalName!=null?'show':''}}" wx:if="{{showLists}}" style="opacity:{{0.85}}">
+      <view class="cu-list grid col-{{iconCls}}">
+        <view class="cu-item" wx:for="{{iconList}}" wx:key="{{item.method}}" bindtap='clickItem' data-id="{{item.method}}">
+          <view class="cuIcon-{{item.icon}} text-{{item.color}}" data-id="{{item.method}}">
+            <view class="cu-tag badge" wx:if="{{item.badge!=0}}">
+              <block wx:if="{{item.badge!=1}}">{{item.badge>99?"99+":item.badge}}</block>
+            </view>
+          </view>
+          <text data-id="{{item.method}}">{{item.name}}</text>
+        </view>
+      </view>
+      <view class="cu-bar bg-white solid-bottom margin-top">
+        <view class="action">
+          已保存密码
+        </view>
+      </view>
+      <view class="cu-list menu-avatar">
+        <view class="cu-item {{modalName=='move-box-'+ index?'move-cur':''}}" wx:for="{{lists}}" wx:key bindtouchstart="ListTouchStart" bindtouchmove="ListTouchMove" bindtouchend="ListTouchEnd" data-target="move-box-{{index}}">
+          <view class="cu-avatar lg round small-font {{colors[(index+1%colors.length)]}}" data-id="{{item.id}}" bindtap='editPwd'>{{item.name}}</view>
+          <view class="content">
+            <view class="text-grey">账户 - {{item.account}}</view>
+            <view class="text-gray text-sm text-cut">
+              <text class="cuIcon-infofill text-red" bindtap='copyPwd' data-val="{{item.password}}">查看密码</text> - {{item.password}}</view>
+          </view>
+          <view class="action">
+            <view class="text-grey text-xs">{{item.create_time}}</view>
+          </view>
+          <view class="move">
+            <view class="bg-grey" data-id="{{item.id}}" data-name="{{item.name}}" bindtap='editPwd'>编辑</view>
+            <view class="bg-red" data-id="{{item.id}}" data-name="{{item.name}}" bindtap='removePwd'>删除</view>
+          </view>
+        </view>
+
+        <view class="cu-load bg-white {{!isLoad?'loading':'over'}} {{hiddenLoading?'hidden':'show'}}"></view>
+      </view>
+      <view class='padding middle-opt white'>操作指南:左滑编辑或删除数据</view>
+    </scroll-view>
+    <view class="usermotto" class="{{hiddenStatus?'hidden':'show'}}">
+      <text class="user-motto font-18 white">{{motto}}</text>
+    </view>
+  </view>
+  <view class="theme">
+    <view class="cu-bar bg-white">
+      <view class="action">
+        <text class="cuIcon-title text-pink"></text> 点击图片设置成背景
+      </view>
+    </view>
+    <swiper class="screen-swiper {{DotStyle?'square-dot':'round-dot'}}" indicator-dots="true" circular="true" autoplay="true" interval="5000" duration="500">
+      <swiper-item wx:for="{{swiperList}}" wx:key data-src="{{item.url}}" bindtap='setToBackground'>
+        <image src="{{item.url}}" mode="aspectFill" wx:if="{{item.type=='image'}}"></image>
+        <video src="{{item.url}}" autoplay loop muted show-play-btn="{{false}}" controls="{{false}}" objectFit="cover" wx:if="{{item.type=='video'}}"></video>
+      </swiper-item>
+    </swiper>
+  </view>
+  <view class='padding tab-bottom bg-gray align-center text-center'>已经到底了</view>
+</home>
+
+<view class="cu-bar tabbar bg-white shadow foot">
+  <view class="action" bindtap="NavChange" data-cur="passwd-note">
+    <view class='cuIcon-cu-image'>
+      <image src="/images/tabbar/component{{PageCur=='passwd-note'?'_cur':''}}.png"></image>
+    </view>
+    <view class="{{PageCur=='passwd-note'?'text-green':'text-gray'}}">我的密码</view>
+  </view>
+  <view class="action" bindtap="NavChange" data-cur="about">
+    <view class='cuIcon-cu-image'>
+      <image src="/images/tabbar/about{{PageCur=='about'?'_cur':''}}.png"></image>
+    </view>
+    <view class="{{PageCur=='about'?'text-green':'text-gray'}}">关于</view>
+  </view>
+</view>

+ 50 - 0
pages/index/index.wxss

@@ -0,0 +1,50 @@
+/**index.wxss**/
+
+.userinfo {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  margin-bottom: 20rpx;
+}
+
+.userinfo-avatar {
+  width: 128rpx;
+  height: 128rpx;
+  margin: 20rpx;
+  border-radius: 50%;
+}
+.userinfo-avatar-unauth{
+  width: 158rpx;
+  height: 158rpx;
+  margin: 20rpx;
+}
+
+.userinfo-nickname {
+  color: #aaa;
+}
+
+.usermotto {
+  margin-top: 200px;
+}
+
+.userinfo-avatar-unauth image {
+  width: 130rpx;
+  height: 130rpx;
+  max-width: none;
+}
+
+
+.white {
+  color: #fff;
+}
+.middle-opt{
+  padding-top:55rpx;
+}
+
+.gray {
+  color: gray;
+}
+
+.theme {
+  background: #fff;
+}

+ 125 - 0
pages/keys/keys.js

@@ -0,0 +1,125 @@
+// pages/keys/keys.js
+const app = getApp()
+const appConfig = require('../../config');
+const common = require('../../common');
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+    publicKey : "",
+    privateKey : "",
+    background: "",
+  },
+  savePrivateKey : function(){
+    if(this.data.publicKey != "" && this.data.privateKey != ""){
+      common.saveKeys(this)
+    }
+  },
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function (options) {
+    var _this = this;
+    _this.setData({
+      background: common.getBackground()
+    });
+    wx.setNavigationBarTitle({
+      title: '创建公私钥'
+    });
+    //如果已经有公私钥了就退出当前页面
+
+    var sslKeys = wx.getStorageSync(appConfig.storeKeys.sslKeys)
+    var serverInfo = wx.getStorageSync(appConfig.storeKeys.serverInfo)
+    if (serverInfo['sslKeys'] && sslKeys['publicKey'] == serverInfo['sslKeys']['publicKey']){
+      wx.showModal({
+        title: '温馨提示',
+        content: '您已经创建了公私钥,如果丢失,请重置公私钥~',
+        success: function(item){
+          app.globalData.isReloadIcon = true;
+          wx.navigateBack();
+        }
+      })
+      return;
+    }
+    app.sslKeyRequestSync = res => {
+      if (res && res.code == 0) {
+        _this.setData({
+          publicKey: res.data.publicKey,
+          privateKey: res.data.privateKey,
+        });
+      }else if(res && res.code == 5) {
+        //force update
+      }
+    }
+    app.sslKeySaveSync = res => {
+      app.log('sslKeySaveSync', res);
+      //保存公私钥到本地
+      wx.setStorageSync(appConfig.storeKeys.sslKeys, { 'publicKey': this.data.publicKey, 'privateKey': this.data.privateKey });
+      //更新serverInfo中用户的公私钥
+      var serverInfo = wx.getStorageSync(appConfig.storeKeys.serverInfo);
+      serverInfo.sslKeys = { 'publicKey': this.data.publicKey, 'privateKey': this.data.privateKey };
+      wx.setStorageSync(appConfig.storeKeys.serverInfo, serverInfo);
+      app.globalData.isReloadIcon = true;
+      wx.showModal({
+        title: '温馨提示',
+        content: res.msg || "保存成功",
+        showCancel: false,
+        success: function(item){
+          wx.navigateBack();
+        }
+      })
+    }
+    common.genKeys(this);
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload: function () {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh: function () {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom: function () {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function () {
+
+  }
+})

+ 5 - 0
pages/keys/keys.json

@@ -0,0 +1,5 @@
+{
+  "usingComponents": {
+    "background": "/pages/components/background/background"
+  }
+}

+ 26 - 0
pages/keys/keys.wxml

@@ -0,0 +1,26 @@
+<background background="{{background}}"></background>
+<view class="font-18" style="opacity:{{0.85}}">
+<view class="cu-bar bg-white solid-bottom margin-top">
+  <view class="action">
+    <text class="cuIcon-title text-orange "></text> 公钥
+  </view>
+</view>
+<view class="cu-form-group">
+  <textarea name="note" id="note" maxlength="-1" readonly bindinput="textareaAInput" placeholder="公钥" bindinput="inputAction" data-id="public_key" value="{{publicKey}}"></textarea>
+</view>
+<view class="cu-bar bg-white solid-bottom margin-top">
+  <view class="action">
+    <text class="cuIcon-title text-orange "></text> 私钥
+  </view>
+</view>
+<view class="cu-form-group">
+  <textarea name="note" id="note" maxlength="-1" disabled="{{modalName!=null}}" bindinput="textareaAInput" placeholder="私钥" bindinput="inputAction" data-id="private_key" value="{{privateKey}}"></textarea>
+</view>
+
+<view class="padding flex flex-wrap justify-between align-center margin-top">
+  <button class="cu-btn bg-red round justify-middle" bindtap='savePrivateKey'>保存私钥到本地</button>
+</view>
+</view>
+<view class="margin-top margin-20 font-18 red">
+  请妥善保存您的私钥,一旦私钥丢失,所有密码将无法解密。
+</view>

+ 1 - 0
pages/keys/keys.wxss

@@ -0,0 +1 @@
+/* pages/keys/keys.wxss */

+ 219 - 0
pages/password/password.js

@@ -0,0 +1,219 @@
+// pages/password/password.js
+const app = getApp()
+const appConfig = require('../../config');
+const common = require('../../common');
+const rand = require('../../js/rand');
+const utils = require('../../utils/util');
+const cache = require('../../js/cache');
+
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+    id: "",
+    name: "",
+    password: "",
+    account: "",
+    link: "",
+    note: "",
+    pubKey: "",
+    background: "",
+    isEdit : false,
+    passwordLength : 12,
+  },
+  inputAction: function(e) {
+    var name = e.target.dataset.id;
+    var data = {};
+    app.log(name);
+    if (name == 'passwordLength') {
+      if(e.detail.value < 6) {
+        data[name] = 6;
+        this.setData(data);
+        return;
+      }
+    }
+    data[name] = e.detail.value;
+    this.setData(data);
+  },
+  getBack: function() {
+    wx.navigateBack();
+  },
+  randPassword: function() {
+    var passwd = rand.randCombin("alpha,smallLetter,bigLetter,specialChars", this.data.passwordLength, true);
+    app.log('password', passwd);
+    this.setData({
+      password: passwd
+    });
+  },
+  minusLength: function(){
+    if(this.data.passwordLength <= 6) {
+      return;
+    }
+    this.data.passwordLength = this.data.passwordLength - 1;
+    this.setData({
+      passwordLength: this.data.passwordLength
+    });
+    this.randPassword();
+  },
+  addLength: function () {
+    if(this.data.passwordLength >= 32) {
+      return;
+    }
+    this.data.passwordLength = this.data.passwordLength + 1;
+    this.setData({
+      passwordLength: this.data.passwordLength
+    });
+    this.randPassword();
+  },
+  submitPassword: function() {
+    var submitData = {};
+    submitData.id = this.data.id;
+    submitData.name = this.data.name;
+    submitData.account = this.data.account;
+    submitData.password = this.data.password;
+    submitData.link = this.data.link;
+    submitData.note = this.data.note;
+    submitData.pubKey = this.data.pubKey;
+    submitData.isEdit = this.data.isEdit;
+    app.log("submitData", submitData);
+    common.savePwd(this, submitData);
+  },
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function(options) {
+    this.setData({
+      background: utils.getBackground()
+    });
+    wx.setNavigationBarTitle({
+      title: '创建我的密码'
+    });
+    var serverInfo = wx.getStorageSync("serverInfo");
+    app.sslKeyDecryptSync = res => {
+      app.log(res)
+      this.setData(app.globalData.info, utils.getPrivateKey());
+      if (!res || (res.code && res.code > 0)) {
+        app.warning(res.msg || "解密失败,请确认您的私钥是否正确~");
+        this.setData({
+          password: "",
+        });
+        return;
+      }
+      this.setData({
+        password: res.data.password
+      });
+    }
+    app.storeSuccess = res => {
+      if (!res || (res.code && res.code > 0)) {
+        app.warning(res.msg || "保存失败");
+        return;
+      }
+      //如果是线上的话就编辑本地数据
+      if (!utils.getSwitchToLocalStatus()) {
+        cache.editSingle(res.data);
+      }
+      wx.showModal({
+        title: '温馨提示',
+        content: res.msg || "保存成功",
+        showCancel: false,
+        success: function(item) {
+          if (item.confirm) {
+            app.globalData.isReloadLists = true;
+            setTimeout(function() {
+              wx.navigateBack();
+            }, appConfig.duration);
+          }
+        }
+      })
+    }
+    app.log("serverInfo", serverInfo)
+    if (app.globalData.info != null && typeof app.globalData.info == 'object') {
+      //去掉服务端解密过程
+      //common.decrypt(app.globalData.info.password, serverInfo.sslKeys.privateKey);
+      var res = { "code": 0, "msg": "解密成功" };
+      try {
+        var decrypt = utils.sslDecrypt(app.globalData.info.password, serverInfo.sslKeys.privateKey)
+        if (decrypt == false) {
+          res = { "code": 1, "msg": "解密失败,请检查您的私钥及密码串~" };
+        } else {
+          res.data = { password: decrypt };
+        }
+      } catch (e) {
+        res.code = 400;
+        res.msg = "解密失败,请检查您的私钥及密码串~";
+      }
+      app.sslKeyDecryptSync(res);
+
+      wx.setNavigationBarTitle({
+        title: '编辑我的密码',
+      });
+      this.setData({
+        isEdit : true
+      });
+      app.log("app.globalData.info", typeof app.globalData.info);
+    }
+    this.setData({
+      'pubKey': serverInfo.sslKeys.publicKey
+    });
+  },
+  SetShadow(e) {
+    this.setData({
+      shadow: e.detail.value
+    })
+  },
+  SetBorderSize(e) {
+    this.setData({
+      bordersize: e.detail.value
+    })
+  },
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady: function() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow: function() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide: function() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload: function() {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh: function() {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom: function() {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function() {
+
+  }
+})

+ 5 - 0
pages/password/password.json

@@ -0,0 +1,5 @@
+{
+  "usingComponents": {
+    "background": "/pages/components/background/background"
+  }
+}

+ 44 - 0
pages/password/password.wxml

@@ -0,0 +1,44 @@
+<!--pages/password/password.wxml-->
+<background background="{{background}}"></background>
+<view style="opacity:{{0.85}}">
+  <form>
+    <view class="cu-form-group margin-top">
+      <view class="title">名称</view>
+      <input name="name" id="name" placeholder="名称,用于识别和搜索" bindinput="inputAction" data-id="name" value='{{name}}'>{{name}}</input>
+    </view>
+    <view class="cu-form-group">
+      <view class="title">账户</view>
+      <input name="account" id="account" placeholder="账户名称" bindinput="inputAction" data-id="account" value='{{account}}'></input>
+    </view>
+    <view class='cu-form-group'>
+      <view class='title'>密码长度</view>
+      <text class='cuIcon-pulldown text-orange' bindtap="minusLength"></text>
+      <input class='text-center' type='text' placeholder="密码长度" bindinput="inputAction" id="passwordLength" data-id="passwordLength" value="{{passwordLength}}" disabled='disabled'></input>
+      <text class='cuIcon-pullup text-orange' bindtap="addLength"></text>
+    </view>
+    <view class="cu-form-group">
+      <view class="title">密码</view>
+      <input name="password" id="password" placeholder="密码,将会通过公钥加密存储到服务器" bindinput="inputAction" data-id="password" value='{{password}}'></input>
+      <text class="cuIcon-refresh text-orange" bindtap='randPassword'></text>
+    </view>
+    <view class="cu-form-group">
+      <view class="title">链接</view>
+      <input name="link" id="link" placeholder="网站链接" bindinput="inputAction" data-id="link" value='{{link}}'></input>
+    </view>
+
+    <view class="cu-form-group">
+      <textarea name="note" id="note" maxlength="-1" disabled="{{modalName!=null}}" bindinput="textareaAInput" placeholder="备注" bindinput="inputAction" data-id="note" value='{{note}}'></textarea>
+    </view>
+
+    <view class="cu-form-group margin-top grid col-2 padding-sm">
+      <view class="cu-item" bindtap='getBack'>
+        <button class="cu-btn block line-orange lg bg-blue">
+          <text class="cuIcon-close"></text>取消</button>
+      </view>
+      <view class="cu-item" bindtap='submitPassword'>
+        <button class="cu-btn block line-orange lg bg-blue">
+          <text class="cuIcon-post"></text> 保存 </button>
+      </view>
+    </view>
+  </form>
+</view>

+ 1 - 0
pages/password/password.wxss

@@ -0,0 +1 @@
+/* pages/password/password.wxss */

+ 142 - 0
pages/privatekey/privatekey.js

@@ -0,0 +1,142 @@
+const app = getApp()
+const appConfig = require('../../config')
+const common = require('../../common')
+const utils = require('../../utils/util.js')
+
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+    publicKey : "",
+    privateKey : "",
+    background : "",
+  },
+  inputAction: function (e) {
+    var name = e.target.dataset.id;
+    app.log(name, e.detail.value);
+    var data = {};
+    data[name] = e.detail.value;
+    this.setData(data);
+  },
+  resetPrivateKey : function(){
+    wx.navigateTo({
+      url: '/pages/reset/reset',
+    })
+  },
+  verifyPrivateKey: function(){
+    if(this.data.publicKey == "" || this.data.privateKey == "") {
+      app.warning("公私钥不能为空~")
+      return;
+    }
+    var res = {
+      code : 0, 
+      msg: "恭喜您,您的公私钥校验成功!"
+    };
+    try{
+      if (!utils.verifySslKey(this.data.publicKey, this.data.privateKey)) {
+        res.code = 1;
+        res.msg = "校验失败,您的公私钥不匹配,请查证后再试!";
+      }
+    }catch(e) {
+      app.log(e);
+      res.code = 400;
+      res.msg = e;
+    }
+
+    app.verifyKeysSuccessSync(res);
+
+    //common.verifyKeys(this.data.publicKey, this.data.privateKey);
+  },
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function (options) {
+    this.setData({
+      background: utils.getBackground()
+    });
+    wx.setNavigationBarTitle({
+      title: '验证私钥'
+    });
+    app.verifyKeysSuccessSync = res => {
+      app.log(res)
+      if(!res || (res.code && res.code > 0)) {
+        app.warning(res.msg || "验证失败,请检查您的私钥~");
+        return;
+      }
+      //验证成功有,将公私钥保存到sslKey和serverInfo
+
+      //更新本地公私钥
+      wx.setStorageSync(appConfig.storeKeys.sslKeys, { 'publicKey': this.data.publicKey, 'privateKey': this.data.privateKey });
+
+      //更新serverInfo中用户的公私钥
+      var serverInfo = wx.getStorageSync(appConfig.storeKeys.serverInfo);
+      serverInfo.sslKeys = { 'publicKey': this.data.publicKey, 'privateKey': this.data.privateKey };
+      app.log("更新 serverInfo", serverInfo);
+      wx.setStorageSync(appConfig.storeKeys.serverInfo, serverInfo);
+      wx.showModal({
+        title: '温馨提示',
+        content: '私钥校验成功',
+        showCancel: false,
+        success: function (item) {
+          app.globalData.isReloadIcon = true;
+          wx.navigateBack();
+        }
+      })
+    }
+    var serverInfo = wx.getStorageSync(appConfig.storeKeys.serverInfo);
+    this.setData({
+      "publicKey": utils.getPublicKey()
+    });
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload: function () {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh: function () {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom: function () {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function () {
+
+  }
+})

+ 5 - 0
pages/privatekey/privatekey.json

@@ -0,0 +1,5 @@
+{
+  "usingComponents": {
+    "background": "/pages/components/background/background"
+    }
+}

+ 27 - 0
pages/privatekey/privatekey.wxml

@@ -0,0 +1,27 @@
+<background background="{{background}}"></background>
+<view class="font-18" style="opacity:{{0.85}}">
+<view class="cu-bar bg-white solid-bottom margin-top">
+  <view class="action">
+    <text class="cuIcon-title text-orange "></text>您的公钥
+  </view>
+</view>
+<view class="cu-form-group">
+  <textarea name="note" id="note" maxlength="-1" readonly bindinput="textareaAInput" placeholder="公钥" bindinput="inputAction" data-id="publicKey" value="{{publicKey}}" readonly='true'></textarea>
+</view>
+<view class="cu-bar bg-white solid-bottom margin-top">
+  <view class="action">
+    <text class="cuIcon-title text-orange "></text> 私钥
+  </view>
+</view>
+<view class="cu-form-group">
+  <textarea name="note" id="note" maxlength="-1" disabled="{{modalName!=null}}" bindinput="textareaAInput" placeholder="请输入您保存的私钥" bindinput="inputAction" data-id="privateKey" value="{{privateKey}}"></textarea>
+</view>
+
+<view class="padding flex flex-wrap justify-between align-center margin-top">
+<button class="cu-btn bg-yellow round justify-middle" bindtap='resetPrivateKey'>重置公私钥</button>
+  <button class="cu-btn bg-red round justify-middle" bindtap='verifyPrivateKey'>校验私钥</button>
+</view>
+</view>
+<view class="margin-top margin-20 font-18 red">
+  如果您的私钥丢失,请重置您的公私钥,一旦重置,密码将因为无法解密全部丢失。。
+</view>

+ 1 - 0
pages/privatekey/privatekey.wxss

@@ -0,0 +1 @@
+/* pages/privatekey/privatekey.wxss */

+ 167 - 0
pages/reset/reset.js

@@ -0,0 +1,167 @@
+// pages/keys/keys.js
+const app = getApp()
+const appConfig = require('../../config');
+const common = require('../../common');
+const utils = require('../../utils/util');
+Page({
+  /**
+   * 页面的初始数据
+   */
+  data: {
+    publicKey: "",
+    privateKey: "",
+    opt: "",
+    background: "",
+  },
+  textareaAInput : function(e){
+    var name = e.target.dataset.id;
+    app.log(name, e.detail.value);
+    var data = {};
+    data[name] = e.detail.value;
+    this.setData(data);
+  },
+  resetPrivateKey: function () {
+    var _this = this;
+    
+    wx.showModal({
+      title: '是否要重置公私钥',
+      content: '公私钥将重新生成,您保存的密码将无法解密,请确认是否要进行操作',
+      confirmText: '重置',
+      success: function (item) {
+        if (item.confirm) {
+          var sslKeys = utils.genSslKey();
+          common.saveClientPublicKey(_this, sslKeys['publicKey'], sslKeys['privateKey'])
+        }
+      }
+    })
+    
+  },
+  savePrivateKey: function () {//直接保存用户生成密钥,需要校验公私钥
+    var _this = this;
+    //校验公私钥
+    if (!utils.verifySslKey(_this.data.publicKey, _this.data.privateKey)) {
+      app.warning("您的公私钥不匹配,请查证后再试~");
+      return;
+    }
+    wx.showModal({
+      title: '是否要使用自己公私钥',
+      content: '公私钥保存后,您保存的密码可能无法解密,请确认是否要进行操作',
+      confirmText: '保存',
+      success: function (item) {
+        if (item.confirm) {
+          common.saveClientPublicKey(_this, _this.data.publicKey, _this.data.privateKey)
+        }
+      }
+    })
+  },
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function (options) {
+    var _this = this;
+    _this.setData({
+      background: utils.getBackground()
+    });
+    wx.setNavigationBarTitle({
+      title: '重置私钥'
+    });
+    var serverInfo = wx.getStorageSync("serverInfo");
+    this.setData({
+      publicKey: utils.getPublicKey(),
+      privateKey: utils.getPrivateKey(),
+    });
+    app.sslKeySaveSync = res => {
+      if(!res || (res.code && res.code > 0)){
+        app.warning(res.msg || "更新失败");
+        return;
+      }
+      app.log('sslKeySaveSync', res);
+      this.data.publicKey = res.data.publicKey;
+      this.data.privateKey = res.data.privateKey;
+      this.setData({
+        publicKey : this.data.publicKey,
+        privateKey : this.data.privateKey
+      });
+      //更新本地公私钥
+      wx.setStorageSync(appConfig.storeKeys.sslKeys, { 'publicKey': this.data.publicKey, 'privateKey': this.data.privateKey });
+
+      //更新serverInfo中用户的公私钥
+      var serverInfo = wx.getStorageSync(appConfig.storeKeys.serverInfo);
+      serverInfo.sslKeys = { 'publicKey': this.data.publicKey, 'privateKey': this.data.privateKey };
+      app.log("更新 serverInfo", serverInfo);
+      wx.setStorageSync(appConfig.storeKeys.serverInfo, serverInfo);
+      app.globalData.isReloadLists = true;
+      
+      wx.showModal({
+        title: '温馨提示',
+        content: '请将您的私钥拷贝出来保存到一个安全的地方(点击确定内容将自动复制),以供以后恢复密码使用~',
+        success:function(item){
+          if(item.confirm) {
+            wx.setClipboardData({
+              data: _this.data.privateKey,
+              success: function (res) {
+                wx.getClipboardData({
+                  success: function (res) {
+                  }
+                });
+              },
+              fail: function () {
+                app.toast("复制失败");
+              }
+            });
+          }
+        }
+      });
+      app.globalData.isReloadIcon = true;
+    }
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload: function () {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh: function () {
+    common.genKeys(this);
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom: function () {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function () {
+
+  }
+})

+ 5 - 0
pages/reset/reset.json

@@ -0,0 +1,5 @@
+{
+  "usingComponents": {
+    "background": "/pages/components/background/background"
+  }
+}

+ 27 - 0
pages/reset/reset.wxml

@@ -0,0 +1,27 @@
+<background background="{{background}}"></background>
+<view class="font-18 white" style="opacity:{{0.85}}">
+<view class="cu-bar bg-white solid-bottom margin-top">
+  <view class="action">
+    <text class="cuIcon-title text-orange "></text> 公钥
+  </view>
+</view>
+<view class="cu-form-group">
+  <textarea name="note" id="note" maxlength="-1" readonly bindinput="textareaAInput" placeholder="公钥" data-id="publicKey" value="{{publicKey}}"></textarea>
+</view>
+<view class="cu-bar bg-white solid-bottom margin-top">
+  <view class="action">
+    <text class="cuIcon-title text-orange "></text> 私钥
+  </view>
+</view>
+<view class="cu-form-group">
+  <textarea name="note" id="note" maxlength="-1" disabled="{{modalName!=null}}" bindinput="textareaAInput" placeholder="私钥" data-id="privateKey" value="{{privateKey}}"></textarea>
+</view>
+
+<view class="padding flex flex-wrap justify-between align-center margin-top">
+  <button class="cu-btn bg-yellow round justify-middle" bindtap='savePrivateKey'>保存</button>
+  <button class="cu-btn bg-red round justify-middle" bindtap='resetPrivateKey'>重置公私钥</button>
+</view>
+</view>
+<view class="margin-top margin-20 font-18 red">
+  您正在重置您的公私钥,一旦重置,所有已保存的密码将全部丢失。服务端仅保存您的公钥用于加密使用,请您妥善保管您的私钥。
+</view>

+ 1 - 0
pages/reset/reset.wxss

@@ -0,0 +1 @@
+/* pages/reset/reset.wxss */

+ 55 - 0
project.config.json

@@ -0,0 +1,55 @@
+{
+	"description": "项目配置文件",
+	"packOptions": {
+		"ignore": []
+	},
+	"setting": {
+		"urlCheck": false,
+		"es6": true,
+		"postcss": true,
+		"minified": true,
+		"newFeature": true,
+		"coverView": true,
+		"autoAudits": false,
+		"checkInvalidKey": true,
+		"checkSiteMap": true,
+		"uploadWithSourceMap": true,
+		"babelSetting": {
+			"ignore": [],
+			"disablePlugins": [],
+			"outputPath": ""
+		}
+	},
+	"compileType": "miniprogram",
+	"libVersion": "2.7.0",
+	"appid": "wx3caf868dab62f709",
+	"projectname": "passwd",
+	"cloudfunctionTemplateRoot": "",
+	"debugOptions": {
+		"hidedInDevtools": []
+	},
+	"scripts": {},
+	"simulatorType": "wechat",
+	"simulatorPluginLibVersion": {},
+	"condition": {
+		"search": {
+			"current": -1,
+			"list": []
+		},
+		"conversation": {
+			"current": -1,
+			"list": []
+		},
+		"plugin": {
+			"current": -1,
+			"list": []
+		},
+		"game": {
+			"list": []
+		},
+		"miniprogram": {
+			"current": -1,
+			"list": []
+		}
+	}
+}

+ 7 - 0
sitemap.json

@@ -0,0 +1,7 @@
+{
+  "desc": "关于本文件的更多信息,请参考文档 https://www.travelzs.com/password.html",
+  "rules": [{
+  "action": "allow",
+  "page": "*"
+  }]
+}

+ 305 - 0
utils/util.js

@@ -0,0 +1,305 @@
+const app =  getApp();
+const appConfig = require("../config")
+const Encrypt = require("../js/jsencrypt")
+
+function http_build_query(formdata, numeric_prefix, arg_separator) {
+  var value, key, tmp = [],
+    that = this;
+  var _http_build_query_helper = function (key, val, arg_separator) {
+    var k, tmp = [];
+    if (val === true) {
+      val = '1';
+    } else if (val === false) {
+      val = '0';
+    }
+    if (val != null) {
+      if (typeof val === 'object') {
+        for (k in val) {
+          if (val[k] != null) {
+            tmp.push(_http_build_query_helper(key + '[' + k + ']', val[k], arg_separator));
+          }
+        }
+        return tmp.join(arg_separator);
+      } else if (typeof val !== 'function') {
+        key = urldecode(key),
+          val = urldecode(val); //有可能参数之前是encode过  add by fengwei
+        return urlencode(key) + '=' + urlencode(val);
+      } else {
+        throw new Error('There was an error processing for http_build_query().');
+      }
+    } else {
+      return '';
+    }
+  };
+
+  if (!arg_separator) {
+    arg_separator = '&';
+  }
+  for (key in formdata) {
+    value = formdata[key];
+    if (numeric_prefix && !isNaN(key)) {
+      key = String(numeric_prefix) + key;
+    }
+    var query = _http_build_query_helper(key, value, arg_separator);
+    if (query !== '') {
+      tmp.push(query);
+    }
+  }
+  return tmp.join(arg_separator);
+}
+//如php urlencode.
+function urlencode(str) {
+  str += '';
+  return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
+}
+//php urldecode.
+function urldecode(str) {
+  try {
+    return decodeURIComponent((str + '').replace(/%(?![\da-f]{2})/gi,
+      function () {
+        return '%25';
+      }).replace(/\+/g, '%20'));
+  } catch (e) {
+    return '';
+  }
+}
+
+const formatTime = date => {
+  const year = date.getFullYear()
+  const month = date.getMonth() + 1
+  const day = date.getDate()
+  const hour = date.getHours()
+  const minute = date.getMinutes()
+  const second = date.getSeconds()
+
+  return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
+}
+
+const formatNumber = n => {
+  n = n.toString()
+  return n[1] ? n : '0' + n
+}
+
+var showCreateTime = function(){
+  var date = new Date();
+  const month = date.getMonth() + 1
+  const day = date.getDate()
+  const hour = date.getHours()
+  const minute = date.getMinutes()
+
+  return [month, day].map(formatNumber).join('-') + ' ' + [hour, minute].map(formatNumber).join(':');
+}
+var wordwrap = function (str, width) {
+  width = width || 64;
+  if (!str) {
+    return str;
+  }
+  var regex = "(.{1," + width + "})( +|$\n?)|(.{1," + width + "})";
+  return str.match(RegExp(regex, "g")).join("\n");
+};
+var sslEncrypt = function(str, publicKey) {
+  return this.encrypt(str, publicKey);
+}
+var sslDecrypt = function (str, privateKey) {
+  //格式化js使用的private key格式,服务端返回的跟本地使用的不一致
+  privateKey = privateKey.replace("-----BEGIN RSA PRIVATE KEY-----", "-----BEGIN PRIVATE KEY-----");
+  privateKey = privateKey.replace("-----END RSA PRIVATE KEY-----", "-----END PRIVATE KEY-----");
+  return this.decrypt(str, privateKey);
+}
+//验证公私钥
+var verifySslKey = function(publicKey, privateKey){
+  app.log(publicKey, privateKey);
+  var verifyStr = "It's ok";
+  var encrypt = this.encrypt(verifyStr, publicKey);
+  var decrypt = this.decrypt(encrypt, privateKey);
+  if (decrypt != verifyStr) {
+    return false;
+  }
+  return true;
+}
+
+var genSslKey = function(){
+  var crypt = new Encrypt.JSEncrypt({ default_key_size: 1024 });
+  crypt.getKey();
+  var publicKey = crypt.getPublicKey();
+  var privateKey = crypt.getPrivateKey();
+  // 去除-----*** RSA **** KEY----- 和空格换行
+  //publicKey = (publicKey.split('-----'))[2];
+  //publicKey = publicKey.replace(/\n/g, "").replace(/\r/g, "").replace(/\t/g, "").replace(/\s*/g, "");
+  //privateKey = (privateKey.split('-----'))[2];
+  //privateKey = privateKey.replace(/\n/g, "").replace(/\r/g, "").replace(/\t/g, "").replace(/\s*/g, "");
+  return {
+    publicKey : publicKey,
+    privateKey : privateKey
+  };
+}
+var encrypt = function (str, publicKey) {
+  var crypt = new Encrypt.JSEncrypt();
+  crypt.setPublicKey(publicKey);
+  return crypt.encrypt(str);
+}
+
+var decrypt = function(str, privateKey){
+  var crypt = new Encrypt.JSEncrypt();
+  crypt.setPrivateKey(privateKey);
+
+  var decStr = crypt.decrypt(str);
+  if(decStr == null) return false;
+  return decStr;
+}
+
+var getUrl = function(api, params) {
+  if(typeof params == 'undefined')
+  {
+    params = {};
+  }
+  if (!appConfig.api[api]) {
+    return "";
+  }
+  params.isLocal = appConfig.api[api].indexOf("localhost") != -1 ? 1 : 0;
+  if (appConfig.api[api].indexOf("?") !== -1) {
+    return appConfig.api[api] + "&" + http_build_query(params);
+  }
+  return appConfig.api[api] + "?" + http_build_query(params);
+}
+var getSwitchToLocalStatus = function() {
+  var localStatus = wx.getStorageSync(appConfig.storeKeys.switchToLocal) || 0;
+  if(localStatus == 1) {
+    return true;
+  }
+  return false;
+}
+
+
+var syncServerInfo = function() { //同步更新serverInfo和sslKey
+  var sslKeys = wx.getStorageSync(appConfig.storeKeys.sslKeys)
+  var serverInfo = wx.getStorageSync(appConfig.storeKeys.serverInfo)
+  app.log("syncServerInfo", sslKeys, serverInfo);
+  if (!serverInfo) return false;
+  if (sslKeys && sslKeys['publicKey'] && sslKeys['privateKey'] &&
+    serverInfo && serverInfo['sslKeys'] && (sslKeys['publicKey'] == serverInfo['sslKeys']['publicKey'])
+  ) {
+    serverInfo['sslKeys']['privateKey'] = sslKeys['privateKey'];
+    wx.setStorageSync(appConfig.storeKeys.serverInfo, serverInfo);
+  }
+  return serverInfo;
+}
+
+var hasPublicKey = function() {
+  var serverInfo = this.syncServerInfo();
+  if (!serverInfo) {
+    return false;
+  }
+  if (!serverInfo['sslKeys']) {
+    return false;
+  }
+  if (!serverInfo['sslKeys']['publicKey'] || serverInfo['sslKeys']['publicKey'] == "") {
+    return false;
+  }
+  return true;
+}
+
+var hasPrivateKey = function() {
+  var serverInfo = this.syncServerInfo();
+  if (!serverInfo) {
+    return false;
+  }
+  if (!serverInfo['sslKeys']) {
+    return false;
+  }
+  if (!serverInfo['sslKeys']['privateKey'] || serverInfo['sslKeys']['privateKey'] == "") {
+    return false;
+  }
+  return true;
+}
+
+///
+var getPublicKey = function() {
+  var serverInfo = this.syncServerInfo();
+  if (!serverInfo) {
+    return "";
+  }
+  if (!serverInfo['sslKeys']) {
+    return "";
+  }
+  if (!serverInfo['sslKeys']['publicKey'] || serverInfo['sslKeys']['publicKey'] == "") {
+    return "";
+  }
+  return serverInfo['sslKeys']['publicKey'];
+}
+var getPrivateKey = function() {
+  var serverInfo = this.syncServerInfo();
+  if (!serverInfo) {
+    return "";
+  }
+  if (!serverInfo['sslKeys']) {
+    return "";
+  }
+  if (!serverInfo['sslKeys']['privateKey'] || serverInfo['sslKeys']['privateKey'] == "") {
+    return "";
+  }
+  return serverInfo['sslKeys']['privateKey'];
+}
+var hasLogined = function() {
+  var serverInfo = this.syncServerInfo();
+  if (!serverInfo) {
+    return false;
+  }
+  if (serverInfo.sessionId && serverInfo.openId &&
+    serverInfo.sessionId != "" && serverInfo.openId != ""
+  ) {
+    return true;
+  }
+  return false;
+}
+var getSessionId = function() {
+  var serverInfo = this.syncServerInfo();
+  if (!serverInfo) {
+    return "";
+  }
+  if (!serverInfo['sessionId']) {
+    return "";
+  }
+  return serverInfo['sessionId'];
+}
+var getOpenId = function() {
+  var serverInfo = this.syncServerInfo();
+  if (!serverInfo) {
+    return "";
+  }
+  if (!serverInfo['openId']) {
+    return "";
+  }
+  return serverInfo['openId'];
+}
+var setBackground =  function(url) {
+  wx.setStorageSync(appConfig.storeKeys.background, url);
+}
+var getBackground = function() {
+  var background = wx.getStorageSync(appConfig.storeKeys.background);
+  app.log(background);
+  if (!background) return app.globalData.background;
+  return background;
+}
+module.exports = {
+  formatTime: formatTime,
+  sslEncrypt: encrypt,
+  sslDecrypt: decrypt,
+  encrypt: encrypt,
+  decrypt: decrypt,
+  genSslKey: genSslKey,
+  verifySslKey: verifySslKey,
+  getUrl: getUrl,
+  getSwitchToLocalStatus: getSwitchToLocalStatus,
+  showCreateTime: showCreateTime,
+  syncServerInfo: syncServerInfo,
+  hasPublicKey: hasPublicKey,
+  hasPrivateKey: hasPrivateKey,
+  getPublicKey: getPublicKey,
+  getPrivateKey: getPrivateKey,
+  hasLogined: hasLogined,
+  getSessionId: getSessionId,
+  getOpenId: getOpenId,
+  setBackground: setBackground,
+  getBackground: getBackground,
+}

Some files were not shown because too many files changed in this diff