wcj před 6 roky
rodič
revize
a4a7c59f9c
100 změnil soubory, kde provedl 5557 přidání a 27 odebrání
  1. 6 2
      src/config.json
  2. 3 1
      src/jfw/front/laboratory.go
  3. 469 0
      src/jfw/front/pchelper.go
  4. 8 10
      src/jfw/front/shorturl.go
  5. 4 2
      src/jfw/front/supsearch.go
  6. 2 2
      src/jfw/front/swordfish.go
  7. 1 0
      src/jfw/front/websocket.go
  8. 52 0
      src/jfw/jyutil/jyutil.go
  9. 5 3
      src/jfw/limitsearch/limitSearchText.go
  10. 2 2
      src/jfw/modules/app/src/jfw/front/swordfish.go
  11. 5 1
      src/jfw/modules/app/src/web/templates/weixin/search/mainSearch.html
  12. 5 1
      src/jfw/modules/app/src/web/templates/weixin/wxinfocontent.html
  13. 5 1
      src/jfw/modules/app/src/web/templates/weixin/wxpush.html
  14. 2 2
      src/jfw/modules/entsesearch/src/web/staticres/jylab/entsesearch/js/login.js
  15. 427 0
      src/jfw/modules/pc/client/apple/bg.js
  16. 8 0
      src/jfw/modules/pc/client/apple/config.json
  17. 302 0
      src/jfw/modules/pc/client/apple/css/layout.css
  18. 225 0
      src/jfw/modules/pc/client/apple/css/login.css
  19. 153 0
      src/jfw/modules/pc/client/apple/css/reset.css
  20. binární
      src/jfw/modules/pc/client/apple/img/book.png
  21. binární
      src/jfw/modules/pc/client/apple/img/book2.png
  22. binární
      src/jfw/modules/pc/client/apple/img/book22.png
  23. binární
      src/jfw/modules/pc/client/apple/img/book44.png
  24. binární
      src/jfw/modules/pc/client/apple/img/cancel.png
  25. binární
      src/jfw/modules/pc/client/apple/img/change.png
  26. binární
      src/jfw/modules/pc/client/apple/img/change2.png
  27. binární
      src/jfw/modules/pc/client/apple/img/change22.png
  28. binární
      src/jfw/modules/pc/client/apple/img/head.png
  29. binární
      src/jfw/modules/pc/client/apple/img/headimg.png
  30. binární
      src/jfw/modules/pc/client/apple/img/icon.png
  31. binární
      src/jfw/modules/pc/client/apple/img/icon_16x16@2x.png
  32. binární
      src/jfw/modules/pc/client/apple/img/icon_22x22.png
  33. binární
      src/jfw/modules/pc/client/apple/img/icon_22x22@2x.png
  34. binární
      src/jfw/modules/pc/client/apple/img/icon_32x32@2x.png
  35. binární
      src/jfw/modules/pc/client/apple/img/logo.png
  36. binární
      src/jfw/modules/pc/client/apple/img/logo2.png
  37. binární
      src/jfw/modules/pc/client/apple/img/logout.png
  38. binární
      src/jfw/modules/pc/client/apple/img/logout2.png
  39. binární
      src/jfw/modules/pc/client/apple/img/logout22.png
  40. binární
      src/jfw/modules/pc/client/apple/img/myicon.png
  41. binární
      src/jfw/modules/pc/client/apple/img/myicon_20_18.png
  42. binární
      src/jfw/modules/pc/client/apple/img/myicon_26_24.png
  43. binární
      src/jfw/modules/pc/client/apple/img/myicon_72_70.png
  44. binární
      src/jfw/modules/pc/client/apple/img/new.png
  45. binární
      src/jfw/modules/pc/client/apple/img/newbook.png
  46. binární
      src/jfw/modules/pc/client/apple/img/newbook22.png
  47. binární
      src/jfw/modules/pc/client/apple/img/newbook44.png
  48. binární
      src/jfw/modules/pc/client/apple/img/search (2).png
  49. binární
      src/jfw/modules/pc/client/apple/img/search.png
  50. binární
      src/jfw/modules/pc/client/apple/img/search22.png
  51. binární
      src/jfw/modules/pc/client/apple/img/smallicon.png
  52. binární
      src/jfw/modules/pc/client/apple/img/top.png
  53. binární
      src/jfw/modules/pc/client/apple/img/top2x.png
  54. binární
      src/jfw/modules/pc/client/apple/img/top@2x.png
  55. binární
      src/jfw/modules/pc/client/apple/img/update.png
  56. binární
      src/jfw/modules/pc/client/apple/img/wx.png
  57. binární
      src/jfw/modules/pc/client/apple/img/yicon.png
  58. binární
      src/jfw/modules/pc/client/apple/img/切换账号@2x.png
  59. binární
      src/jfw/modules/pc/client/apple/img/招标搜索@2x.png
  60. binární
      src/jfw/modules/pc/client/apple/img/订阅消息-有消息@2x.png
  61. binární
      src/jfw/modules/pc/client/apple/img/退出@2x.png
  62. 241 0
      src/jfw/modules/pc/client/apple/js/jyWebScoket.js
  63. 276 0
      src/jfw/modules/pc/client/apple/js/login.js
  64. 420 0
      src/jfw/modules/pc/client/apple/login.html
  65. 15 0
      src/jfw/modules/pc/client/apple/node_modules/.bin/getmac-node
  66. 7 0
      src/jfw/modules/pc/client/apple/node_modules/.bin/getmac-node.cmd
  67. 15 0
      src/jfw/modules/pc/client/apple/node_modules/.bin/semver
  68. 7 0
      src/jfw/modules/pc/client/apple/node_modules/.bin/semver.cmd
  69. 43 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/.npmignore
  70. 33 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/HISTORY.md
  71. 23 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/LICENSE.md
  72. 168 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/README.md
  73. 50 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/es2015/index.js
  74. 30 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/example.js
  75. 3 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/index.js
  76. 16 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/.flowconfig
  77. 44 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/HISTORY.md
  78. 23 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/LICENSE.md
  79. 109 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/README.md
  80. 211 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/es2015/index.js
  81. 169 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/package.json
  82. 190 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/source/index.js
  83. 172 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/package.json
  84. 43 0
      src/jfw/modules/pc/client/apple/node_modules/eachr/source/index.js
  85. 64 0
      src/jfw/modules/pc/client/apple/node_modules/editions/HISTORY.md
  86. 23 0
      src/jfw/modules/pc/client/apple/node_modules/editions/LICENSE.md
  87. 105 0
      src/jfw/modules/pc/client/apple/node_modules/editions/README.md
  88. 188 0
      src/jfw/modules/pc/client/apple/node_modules/editions/edition-browsers/index.js
  89. 188 0
      src/jfw/modules/pc/client/apple/node_modules/editions/edition-node-0.8/index.js
  90. 211 0
      src/jfw/modules/pc/client/apple/node_modules/editions/package.json
  91. 187 0
      src/jfw/modules/pc/client/apple/node_modules/editions/source/index.js
  92. 15 0
      src/jfw/modules/pc/client/apple/node_modules/errlop/HISTORY.md
  93. 23 0
      src/jfw/modules/pc/client/apple/node_modules/errlop/LICENSE.md
  94. 182 0
      src/jfw/modules/pc/client/apple/node_modules/errlop/README.md
  95. 184 0
      src/jfw/modules/pc/client/apple/node_modules/errlop/es2015/index.js
  96. 3 0
      src/jfw/modules/pc/client/apple/node_modules/errlop/index.js
  97. 16 0
      src/jfw/modules/pc/client/apple/node_modules/errlop/node_modules/editions/.flowconfig
  98. 44 0
      src/jfw/modules/pc/client/apple/node_modules/errlop/node_modules/editions/HISTORY.md
  99. 23 0
      src/jfw/modules/pc/client/apple/node_modules/errlop/node_modules/editions/LICENSE.md
  100. 109 0
      src/jfw/modules/pc/client/apple/node_modules/errlop/node_modules/editions/README.md

+ 6 - 2
src/config.json

@@ -9,7 +9,7 @@
     "strTimeNumber": 30,
     "elasticsearch": "http://192.168.3.18:9800",
     "elasticPoolSize": 30,
-    "redisaddrs": "other=192.168.3.18:1379,push=192.168.3.18:1379,sso=192.168.3.18:1379,session=192.168.3.18:1379,recovery=192.168.3.18:1379",
+    "redisaddrs": "other=192.168.3.18:2379,push=192.168.3.18:2379,sso=192.168.3.18:2379,session=192.168.3.18:2379,recovery=192.168.3.18:2379",
     "webport": "8089",
     "webrpcport": "8084",
     "weixinrpc": "127.0.0.1:8083",
@@ -19,7 +19,7 @@
     ],
     "cassandrasize": 5,
     "agreement": "http",
-    "webdomain": "http://webwcj.qmx.top",
+    "webdomain": "http://weblxl.qmx.top",
     "redirect": {
         "searchinfo": "/jylab/mainSearch",
         "rssset": "/swordfish/historypush",
@@ -238,6 +238,10 @@
         "shareTimesUpperLimitIrr": 1000
     },
 	"cookiedomain": ".qmx.top",
+    "pcHelper":{
+        "subscribeTokenTimeout":15,
+        "version":"V2.5.1"
+    },
 	"mysql":{
 		"dbName":   "jianyu",
 		"address":  "192.168.3.207:3366",

+ 3 - 1
src/jfw/front/laboratory.go

@@ -166,10 +166,12 @@ func (l *Lab) QrToLab(t string) error {
 	}
 	redis.Put("other", "qrToLab_"+userId.(string), 1, 10*60)
 	l.SetSession("qrToLab", true)
-	if t != "" && t == "s" {
+	if t == "s" {
 		return l.Redirect("/jylab/laboratory/cjss")
 	} else if t == "e" {
 		return l.Redirect("/jylab/laboratory/zbqy")
+	} else if t == "d" {
+		return l.Redirect("/jylab/laboratory/sjdc")
 	} else {
 		return l.Redirect("/jylab/index")
 	}

+ 469 - 0
src/jfw/front/pchelper.go

@@ -0,0 +1,469 @@
+package front
+
+import (
+	"github.com/go-xweb/xweb"
+	"qfw/util"
+	"strings"
+	"time"
+	"log"
+	"github.com/go-xweb/httpsession"
+	"jfw/jyutil"
+	"fmt"
+	"qfw/util/redis"
+	"net/url"
+	"encoding/base64"
+	"jfw/config"
+)
+
+type PcHelper struct {
+	*xweb.Action
+	login             xweb.Mapper `xweb:"/jypc/login"`         //pc助手登录
+	getWxUT           xweb.Mapper `xweb:"/jcpc/getWxUT"`       //微信登录后token
+	getSubscribeToken xweb.Mapper `xweb:"/jypc/getST"`         //获取订阅记录token
+	pushView          xweb.Mapper `xweb:"/jypc/pushView"`      //订阅记录列表
+	logout            xweb.Mapper `xweb:"/jypc/logout"`        //退出登录
+	needUpdate        xweb.Mapper `xweb:"/jypc/needUpdate"`    //是否需要更新pc助手
+	toAct             xweb.Mapper `xweb:"/jypc/toAct/([^.]*)"` //
+	toPushView        xweb.Mapper `xweb:"/jypc/toPushView"`    //跳转到订阅记录页面
+	toSearch          xweb.Mapper `xweb:"/jypc/toSearch"`      //跳转到招标搜索页面
+}
+
+func init() {
+	xweb.AddAction(&PcHelper{})
+}
+
+var (
+	redisKeyPrefix_PPVOpenid = "pc_push_view_openid_"
+	redisPool                = "other"
+	pcHelperConfig           = config.Sysconfig["pcHelper"].(map[string]interface{})
+	timeout                  = int(pcHelperConfig["subscribeTokenTimeout"].(float64))
+	version                  = pcHelperConfig["version"].(string)
+	redisKey_pcOpenid        = "pc_s_m_openid"
+	othSep                   = "____"
+	md5EncryptSep            = "&"
+	logPrefix                = "PcHelper"
+)
+
+func (l *PcHelper) NeedUpdate() error {
+	defer util.Catch()
+	versionP := l.GetString("version")
+	updateFlag := false //不需要更新
+	if versionP != "" && versionP != version {
+		updateFlag = true
+	}
+	result := map[string]interface{}{
+		"needUpdate": updateFlag,
+		"version":    version,
+	}
+	l.ServeJson(result)
+	return nil
+}
+
+func (l *PcHelper) Login() error {
+	defer util.Catch()
+	//登录页面
+	//if l.Method() == "GET" {
+	//	return l.Render("/pchelper/login.html", nil)
+	//}
+	mac := l.GetString("mac")
+	if mac == "" {
+		return l.Render("/pchelper/login.html", nil)
+	}
+	reToken := ""
+	reOpenId := ""
+	reNickname := ""
+	status := func() int {
+		reqType := l.GetString("reqType")
+		if reqType == "phoneLogin" {
+			phone := l.GetString("phone")
+			password := l.GetString("password")
+			if strings.TrimSpace(phone) == "" || strings.TrimSpace(password) == "" {
+				return -1
+			}
+			query := map[string]interface{}{
+				"i_appid":    2,
+				"s_m_openid": phone,
+				"i_type":     1, //app用户(手机号注册用户)
+				"s_password": util.GetMd5String(password),
+			}
+			user, ok := mongodb.FindOne("user", query)
+			//登录成功
+			if ok && user != nil && len(*user) > 0 {
+				if (*user)["s_m_openid"] != nil {
+					reOpenId = (*user)["s_m_openid"].(string)
+				}
+				if (*user)["s_nickname"] != nil {
+					reNickname = (*user)["s_nickname"].(string)
+				} else if (*user)["s_phone"] != nil {
+					reNickname = (*user)["s_phone"].(string)
+				}
+				reToken = getUToken(mac, phone)
+				return 1
+			}
+			return -1 //用户名或密码不正确
+		} else if reqType == "identCodeLogin" {
+			phone, _ := l.GetSession("identCodeKey").(string)
+			if phone == "" || l.GetSession("identCodeValue") == nil || l.GetString("identCode") != l.GetSession("identCodeValue") { //验证码不正确
+				return -1
+			} else {
+				query := map[string]interface{}{
+					"i_appid":    2,
+					"s_m_openid": phone,
+					"i_type":     1,
+				}
+				user, ok := mongodb.FindOne("user", query)
+				if ok {
+					//用户不存在
+					if user == nil || len(*user) == 0 {
+						data := map[string]interface{}{
+							"i_source":      1, //pc助手注册 add 20181119
+							"i_type":        1, //手机注册
+							"i_appid":       2,
+							"i_ispush":      1,
+							"s_m_openid":    phone,
+							"s_unionid":     phone,
+							"s_phone":       phone,
+							"s_password":    "",
+							"l_registedate": time.Now().Unix(),
+							"i_ts_guide":    2,
+							"o_jy": map[string]interface{}{
+								"i_mode":       1,
+								"i_ratemode":   2,
+								"l_modifydate": time.Now().Unix(),
+							},
+							"o_log": reqPhoneInfo(l.Request),
+						}
+						_id := mongodb.Save("user", data)
+						if _id != "" {
+							deleteIdentSession_(l.Session())
+							reToken = getUToken(mac, phone)
+							reOpenId = phone
+							reNickname = phone
+							return 1
+						}
+					} else {
+						deleteIdentSession_(l.Session())
+						if (*user)["s_m_openid"] != nil {
+							reOpenId = (*user)["s_m_openid"].(string)
+						}
+						if (*user)["s_nickname"] != nil {
+							reNickname = (*user)["s_nickname"].(string)
+						} else if (*user)["s_phone"] != nil {
+							reNickname = (*user)["s_phone"].(string)
+						}
+						reToken = getUToken(mac, phone)
+						return 1
+					}
+				}
+			}
+		} else if reqType == "sendIdentCode" {
+			//phone := jyutil.CheckSendMsg(l.GetString("token"))
+			phone := l.GetString("phone")
+			//手机号验证不通过
+
+			if phone == "" {
+				return -2
+			} else if !phoneReg.MatchString(phone) {
+				return -1
+			} else if SendIdentCode(phone, l.Session()) {
+				return 1
+			}
+			return 0
+		} else if reqType == "autoLogin" {
+			token := l.GetString("token")
+			deMac, phone := decryptUToken(token)
+			if phone != "" && mac == deMac {
+				query := map[string]interface{}{
+					"i_appid":    2,
+					"s_m_openid": phone,
+				}
+				user, ok := mongodb.FindOne("user", query)
+				if ok && user != nil && len(*user) > 0 {
+					if (*user)["s_m_openid"] != nil {
+						reOpenId = (*user)["s_m_openid"].(string)
+					}
+					if (*user)["s_nickname"] != nil {
+						reNickname = (*user)["s_nickname"].(string)
+					} else if (*user)["s_phone"] != nil {
+						reNickname = (*user)["s_phone"].(string)
+					}
+					reToken = getUToken(mac, phone)
+					return 1
+				}
+			} else {
+				return -1
+			}
+		}
+		return 0
+	}()
+
+	result := map[string]interface{}{
+		"status": status,
+	}
+	if status == 1 {
+		result["token"] = reToken
+	}
+	result["openid"] = se.EncodeString(reOpenId)
+	result["nickname"] = reNickname
+	l.ServeJson(result)
+	return nil
+}
+
+func (l *PcHelper) Logout() {
+	defer util.Catch()
+	//wx-login
+	l.DelSession("nickname")
+	l.DelSession("s_nickname")
+	l.DelSession("s_m_openid")
+	l.DelSession("user")
+	l.DelSession("rpcBackUserInfo")
+	//pcHelper-login
+	l.DelSession(redisKey_pcOpenid)
+	deleteIdentSession_(l.Session())
+	l.ServeJson(map[string]interface{}{
+		"status": 1,
+	})
+}
+
+func (l *PcHelper) GetWxUT() error {
+	defer util.Catch()
+	mac := l.GetString("mac")
+	reToken := ""
+	status := 0
+	if mac != "" {
+		reToken = getUToken(mac, l.GetSession("s_m_openid").(string))
+		status = 1
+	}
+	result := map[string]interface{}{
+		"token":  reToken,
+		"status": status,
+	}
+	l.ServeJson(result)
+	return nil
+}
+
+func (l *PcHelper) GetSubscribeToken() error {
+	defer util.Catch()
+	mac := l.GetString("mac")
+	token := l.GetString("token")
+	deMac, phone := decryptUToken(token)
+	reToken := ""
+	status := 0
+	if phone != "" && mac == deMac {
+		reToken = getSToken(deMac, phone)
+		redis.Put(redisPool, redisKeyPrefix_PPVOpenid+phone, 1, timeout)
+		status = 1 //success
+	}
+	result := map[string]interface{}{
+		"token":  reToken,
+		"status": status,
+	}
+	l.ServeJson(result)
+	return nil
+}
+
+func (l *PcHelper) ToAct(token string) error {
+	defer util.Catch()
+	act := l.GetString("act")
+	token = strings.TrimPrefix(l.Url(), "/jypc/toAct/")
+	token = strings.TrimSuffix(token,"?act="+act)
+	if token != "" && act != "" {
+		deMac, phone := decryptSToken(token)
+		b, err := redis.Exists(redisPool, redisKeyPrefix_PPVOpenid+phone)
+		if err != nil {
+			log.Println(logPrefix, "Error redis key "+redisKeyPrefix_PPVOpenid+phone, err.Error())
+		} else {
+			if b {
+				if phone != "" && deMac != "" {
+					l.SetSession(redisKey_pcOpenid, phone)
+					LoginInfo("", phone, l.Session())
+					sessUser := l.GetSession("user").(map[string]interface{})
+					if sessUser != nil {
+						if sessUser["s_nickname"] == nil || sessUser["s_nickname"] == "" {
+							hNickname := phone
+							if len(phone) == 11 {
+								hNickname = phone[0:3] + "****" + phone[7:]
+							}
+							sessUser["s_nickname"] = hNickname
+							l.SetSession("user", sessUser)
+							l.SetSession("nickname", hNickname)
+							l.SetSession("s_nickname", hNickname)
+						}
+					}
+					return l.Redirect("/jypc/" + act)
+				} else {
+					log.Println(logPrefix, "ToAct-token解析异常", "token:", token)
+				}
+			} else {
+				log.Println(logPrefix, "redis key "+redisKeyPrefix_PPVOpenid+phone, "不存在")
+			}
+		}
+	}
+	return l.Redirect("/")
+}
+
+func (l *PcHelper) ToPushView() error {
+	defer util.Catch()
+	return l.Render("/pchelper/pushView.html")
+}
+
+func (l *PcHelper) ToSearch() error {
+	defer util.Catch()
+	return l.Redirect("/jylab/supsearch/index.html")
+}
+
+func (l *PcHelper) PushView() error {
+	defer util.Catch()
+	lasttime, _ := l.GetInt("lasttime")
+	myopenid := ""
+	if l.GetSession(redisKey_pcOpenid) != nil {
+		myopenid, _ = l.GetSession(redisKey_pcOpenid).(string)
+	}
+	log.Println(logPrefix, "PushView-param", "lasttime:", lasttime, "openid:", myopenid)
+	res := map[string]interface{}{}
+	res["success"] = false
+	if myopenid != "" && lasttime > 0 {
+		if lasttime == 1 {
+			lasttime = time.Now().Local().Unix()
+		}
+		thistime, list := getHistorypush(lasttime, 0, myopenid, nil, 0)
+		log.Println(logPrefix, "PushView-getHistorypush-size", len(*list))
+		if list != nil && len(*list) > 0 {
+			res["success"] = true
+			res["data"] = &list
+			res["thistime"] = thistime
+		}
+	}
+	l.ServeJson(&res)
+	return nil
+}
+
+//发送验证码
+func SendIdentCode(phone string, session *httpsession.Session) bool {
+	lastSentTime, _ := session.Get("identCodeTime").(int64)
+	//60秒之内不允许重复发
+	if lastSentTime > 0 && time.Now().Unix()-lastSentTime <= 60 {
+		return false
+	}
+	s_ranNum := util.GetRandom(6) //生成随机数
+	session.Set("identCodeValue", s_ranNum)
+	session.Set("identCodeKey", phone)
+	session.Set("identCodeTime", time.Now().Unix())
+	//发送短信
+	param := make(map[string]string)
+	param["code"] = s_ranNum
+	log.Println("验证码", phone, s_ranNum)
+	jyutil.SendSMS("2175916", phone, param)
+	return true
+}
+
+//删除短信验证码有关的session
+func deleteIdentSession_(session *httpsession.Session) {
+	session.Del("identCodeValue")
+	session.Del("identCodeKey")
+	session.Del("identCodeTime")
+}
+
+func getSToken(params ...string) string {
+	if params != nil {
+		mac := params[0]
+		phone := params[1]
+		now := time.Now()
+		date := util.FormatDate(&now, util.Date_Full_Layout)
+		phone = base64.StdEncoding.EncodeToString([]byte(phone))
+		mxE := util.GetMd5String(mac + md5EncryptSep + phone + md5EncryptSep + date)
+		v := se.EncodeString(mac + othSep + phone + othSep + date + othSep + mxE)
+		return url.QueryEscape(v)
+	}
+	return ""
+}
+
+func decryptSToken(token string) (string, string) {
+	if token == "" {
+		return "", ""
+	}
+	log.Println(logPrefix, "SToken解析前token", token)
+	v, err := url.QueryUnescape(token)
+	if err != nil {
+		log.Println(logPrefix, "SToken QueryUnescape error", err)
+		return "", ""
+	}
+	v = se.DecodeString(v)
+	if err != nil {
+		log.Println(logPrefix, "SToken Decrypt se error", err)
+		return "", ""
+	}
+	log.Println(logPrefix, "SToken解析后token", v)
+	vs := strings.Split(v, othSep)
+	if len(vs) != 4 {
+		log.Println(logPrefix, "SToken Decrypt length error", vs)
+		return "", ""
+	}
+	if vs[3] != util.GetMd5String(fmt.Sprintf("%s"+md5EncryptSep+"%s"+md5EncryptSep+"%s", vs[0], vs[1], vs[2])) {
+		log.Println(logPrefix, "SToken Decrypt mx error", vs)
+		return "", ""
+	}
+	byt, err := base64.StdEncoding.DecodeString(vs[1])
+	openid := ""
+	if err != nil {
+		log.Println(logPrefix, "UToken openid base64 decode error", err.Error())
+	} else {
+		openid = string(byt)
+	}
+	return vs[0], openid
+}
+
+//登录成功后的token
+func getUToken(params ...string) string {
+	if params != nil {
+		mac := params[0]
+		phone := params[1]
+		now := time.Now()
+		date := util.FormatDate(&now, util.Date_Full_Layout)
+		phone = base64.StdEncoding.EncodeToString([]byte(phone))
+		mxE := util.GetMd5String(mac + md5EncryptSep + phone + md5EncryptSep + date)
+		v, err := jyutil.AC.Encrypt(mac + othSep + phone + othSep + date + othSep + mxE)
+		if err != nil {
+			log.Println(logPrefix, "UToken Encrypt error", err)
+			return ""
+		}
+		return url.QueryEscape(v)
+	}
+	return ""
+}
+
+//返回mac phone
+func decryptUToken(token string) (string, string) {
+	if token == "" {
+		return "", ""
+	}
+	log.Println(logPrefix, "UToken解析前token", token)
+	v, err := url.QueryUnescape(token)
+	if err != nil {
+		log.Println(logPrefix, "UToken QueryUnescape error", err)
+		return "", ""
+	}
+	v, err = jyutil.AC.Decrypt(v)
+	if err != nil {
+		log.Println(logPrefix, "UToken Decrypt aes error", err)
+		return "", ""
+	}
+	log.Println(logPrefix, "UToken解析后token", v)
+	vs := strings.Split(v, othSep)
+	if len(vs) != 4 {
+		log.Println(logPrefix, "UToken Decrypt length error", vs)
+		return "", ""
+	}
+	if vs[3] != util.GetMd5String(fmt.Sprintf("%s"+md5EncryptSep+"%s"+md5EncryptSep+"%s", vs[0], vs[1], vs[2])) {
+		log.Println(logPrefix, "UToken Decrypt mx error", vs)
+		return "", ""
+	}
+	byt, err := base64.StdEncoding.DecodeString(vs[1])
+	openid := ""
+	if err != nil {
+		log.Println(logPrefix, "UToken openid base64 decode error", err.Error())
+	} else {
+		openid = string(byt)
+	}
+	return vs[0], openid
+}

+ 8 - 10
src/jfw/front/shorturl.go

@@ -6,7 +6,6 @@ import (
 	"jfw/forceShare"
 	"jfw/tools"
 	"jfw/wx"
-	"log"
 	"math/rand"
 	"net/url"
 	"qfw/util"
@@ -29,8 +28,9 @@ type Short struct {
 var mobileReg = regexp.MustCompile("(?i)(Android|Mobile|Phone)")
 
 var Map_stype = map[string]bool{
-	"content":   true,
-	"bdprivate": true,
+	"content":     true,
+	"bdprivate":   true,
+	"mailprivate": true,
 }
 
 func (s *Short) Article(stype, id string) error {
@@ -250,14 +250,12 @@ func (s *Short) Article(stype, id string) error {
 
 //检查用户是否关注
 func CheckUserIsSubscribe(openid string) bool {
-	log.Println(openid, "------检查是否关注")
-	subscribeLog, subscribe_ok := mongodb.Find("jy_subscribe", map[string]interface{}{
+	user, ok := mongodb.FindOneByField("user", map[string]interface{}{
+		"i_appid":    2,
 		"s_m_openid": openid,
-	}, `{"l_date":-1}`, `{"s_event":1}`, true, -1, -1)
-	if subscribe_ok && subscribeLog != nil && len(*subscribeLog) == 1 && len((*subscribeLog)[0]) > 0 {
-		subscribe_event, _ := (*subscribeLog)[0]["s_event"].(string)
-		if subscribe_event == "unsubscribe" {
-			log.Println(openid, "------已取关!")
+	}, `{"i_ispush":1}`)
+	if ok && user != nil {
+		if util.IntAllDef((*user)["i_ispush"], 1) == 0 {
 			return false
 		}
 	}

+ 4 - 2
src/jfw/front/supsearch.go

@@ -140,12 +140,12 @@ func (p *Pcsearch) ProposedProject() error {
 //
 func (p *Pcsearch) Getstatus() error {
 	defer util.Catch()
-	var supstatus, entstatus, tablepro, dataexportstatus, portraitpower, followent bool
+	var supstatus, entstatus, tablepro, dataexportstatus, portraitpower, followent, smartstatus bool
 	if openid := p.GetSession("openid"); openid != nil {
 		user, b := mongodb.FindOneByField("user", map[string]interface{}{
 			"s_m_openid": openid.(string),
 			"i_appid":    2,
-		}, `{"i_supersearch":1,"i_entsesearch":1,"i_tablepro":1,"i_dataexport":1,"i_portraitpower":1,"i_followent":1}`)
+		}, `{"i_supersearch":1,"i_entsesearch":1,"i_tablepro":1,"i_dataexport":1,"i_portraitpower":1,"i_followent":1,"i_smartset":1}`)
 		if len(*user) > 0 && b {
 			supstatus = util.Int64All((*user)["i_supersearch"]) == 1
 			entstatus = util.Int64All((*user)["i_entsesearch"]) == 1
@@ -153,9 +153,11 @@ func (p *Pcsearch) Getstatus() error {
 			dataexportstatus = util.Int64All((*user)["i_dataexport"]) == 1
 			portraitpower = util.Int64All((*user)["i_portraitpower"]) == 1
 			followent = util.Int64All((*user)["i_followent"]) == 1
+			smartstatus = util.Int64All((*user)["i_smartset"]) == 1
 		}
 	}
 	p.ServeJson(map[string]interface{}{
+		"smartstatus":      smartstatus,
 		"supstatus":        supstatus,
 		"entstatus":        entstatus,
 		"tablepro":         tablepro,

+ 2 - 2
src/jfw/front/swordfish.go

@@ -2820,7 +2820,7 @@ func getShouldQueryMap(findfield, searchvalue, industry string) []map[string]str
 }
 
 func getSearchQuery(keyword, industry, minprice, maxprice, findfields, mustquery string) (qstr string) {
-	multi_match := `{"multi_match": {"query": "%s","type": "phrase", "fields": [%s],"analyzer": "my_ngram"}}`
+	multi_match := `{"multi_match": {"query": "%s","type": "phrase", "fields": [%s]}}`
 	//match_phrase := `{"match_phrase": {"s_subscopeclass": "%s"}}`
 	query := `{"query":{"bool":{"must":[%s],"should":[%s],"minimum_should_match": %d}}}`
 	query_bool_should := `{"bool":{"should":[%s],"minimum_should_match": 1}}`
@@ -3026,7 +3026,7 @@ func stringMForm(s string) string {
 func wxPushViewDatas(index, itype string, keys []elastic.KeyConfig, allquery, findfields, SortQuery, fields string, start, limit int) *[]map[string]interface{} {
 	query_all := `{"query":{"bool":{"must":[%s],"should":[%s],"minimum_should_match":1}}}`
 	match_detail := `{"match":{"detail":{"query":"%s","operator": "and"}}}`
-	minq := `{"multi_match": {"query": "%s","type": "phrase", "fields": [%s],"analyzer": "my_ngram"}}`
+	minq := `{"multi_match": {"query": "%s","type": "phrase", "fields": [%s]}}`
 	highlightStr := `%s: {"fragment_size": %d,"number_of_fragments": 1}`
 	query_bool := `{"bool":{"must":[%s],"should":[%s],"must_not":[%s],"minimum_should_match":1}}`
 	bool_must := `{"bool":{"must":[%s]}}`

+ 1 - 0
src/jfw/front/websocket.go

@@ -150,6 +150,7 @@ func LoginInfo(shareid, openid string, Sess interface{}) (infoData map[string]in
 			infoData["redisheadimg"] = fmt.Sprint(redisheadimg)
 			infoData["encryptId"] = se.EncodeString(qutil.BsonIdToSId((*user)["_id"]))
 			infoData["shareid"] = shareid
+			infoData["openid"] = se.EncodeString(fmt.Sprint((*user)["s_m_openid"]))//add 20181116
 			(*user)["shareid"] = shareid
 			nick := fmt.Sprint((*user)["s_nickname"])
 			sess.Set("nickname", nick)

+ 52 - 0
src/jfw/jyutil/jyutil.go

@@ -12,6 +12,7 @@ import (
 	. "qfw/util/sms"
 	"strings"
 	"time"
+	"net/url"
 )
 
 //计数器
@@ -26,6 +27,11 @@ var ActivityEndCode, ActivityStartCode int64
 
 var jy_activeset map[string]interface{}
 
+var AC = &util.AES_CBC{
+	Key: "mGlAgnIBB8bx2nch",
+	Iv:  "1389461544135476",
+}
+
 func init() {
 	jy_activeset = config.Sysconfig["jy_activeset"].(map[string]interface{})
 }
@@ -155,3 +161,49 @@ func SendSMS(tplcode /*模板代码*/, mobile /*手机号码*/ string, param map
 	text := strings.Join(tmp, "&")
 	SendSms(mobile, tplcode, text)
 }
+
+//发短信,验证token
+func CheckSendMsg(token string) string {
+	if token == "" {
+		return ""
+	}
+	log.Println("短信解析前token", token)
+	key := fmt.Sprintf("smstoken_%s", token)
+	ok, e := redis.Exists("other", key)
+	if e != nil {
+		log.Println("redis中token error", e)
+		return ""
+	}
+	if ok {
+		log.Println("redis中token已存在", token)
+		return ""
+	}
+	token, e = url.QueryUnescape(token)
+	if e != nil {
+		log.Println("短信token QueryUnescape error", e)
+	}
+	v, err := AC.Decrypt(token)
+	if err != nil {
+		log.Println("短信token Decrypt error", err)
+		return ""
+	}
+	log.Println("短信解析后token", v)
+	vs := strings.Split(v, "_")
+	if len(vs) != 3 {
+		log.Println("短信token error", vs)
+		return ""
+	}
+	now := time.Now()
+	if !strings.HasPrefix(vs[1], util.FormatDate(&now, util.Date_yyyyMMdd)) {
+		log.Println("短信token date错误", vs)
+		return ""
+	}
+	if vs[2] != util.GetMd5String(fmt.Sprintf("%s&%s", vs[0], vs[1])) {
+		log.Println("短信token sing错误", vs)
+		return ""
+	}
+	if vs[0] != "" {
+		redis.Put("other", key, 1, 86400)
+	}
+	return vs[0]
+}

+ 5 - 3
src/jfw/limitsearch/limitSearchText.go

@@ -61,9 +61,11 @@ func (l *LimitSearchText) IsLimited(r *http.Request, w http.ResponseWriter, s *h
 	if l.TimeOut > 0 {
 		limitFlag, isNew := l.getFlag(r, w, s)
 		if isNew {
-			timeLimit, _ := redis.Exists("other", "jy_limitSearchText_"+limitFlag)
-			if timeLimit {
-				return -1
+			if int(redis.LLEN("other", "jy_limitSearchText")) <= l.Count/2 {
+				timeLimit, _ := redis.Exists("other", "jy_limitSearchText_"+limitFlag)
+				if timeLimit {
+					return -1
+				}
 			}
 		}
 		redis.Put("other", "jy_limitSearchText_"+limitFlag, 1, l.TimeOut)

+ 2 - 2
src/jfw/modules/app/src/jfw/front/swordfish.go

@@ -383,7 +383,7 @@ func (f *Front) MyFeedbacks() error {
 }
 
 func getSearchQuery(keyword, industry, minprice, maxprice, findfields, mustquery string) (qstr string) {
-	multi_match := `{"multi_match": {"query": "%s","type": "phrase", "fields": [%s],"analyzer": "my_ngram"}}`
+	multi_match := `{"multi_match": {"query": "%s","type": "phrase", "fields": [%s]}}`
 	//match_phrase := `{"match_phrase": {"s_subscopeclass": "%s"}}`
 	query := `{"query":{"bool":{"must":[%s],"should":[%s],"minimum_should_match": %d}}}`
 	query_bool_should := `{"bool":{"should":[%s],"minimum_should_match": 1}}`
@@ -442,7 +442,7 @@ func getSearchQuery(keyword, industry, minprice, maxprice, findfields, mustquery
 func wxPushViewDatas(index, itype string, keys []elastic.KeyConfig, allquery, findfields, SortQuery, fields string, start, limit int) *[]map[string]interface{} {
 	query_all := `{"query":{"bool":{"must":[%s],"should":[%s],"minimum_should_match":1}}}`
 	match_detail := `{"match":{"detail":{"query":"%s","operator": "and"}}}`
-	minq := `{"multi_match": {"query": "%s","type": "phrase", "fields": [%s],"analyzer": "my_ngram"}}`
+	minq := `{"multi_match": {"query": "%s","type": "phrase", "fields": [%s]}}`
 	highlightStr := `%s: {"fragment_size": %d,"number_of_fragments": 1}`
 	query_bool := `{"bool":{"must":[%s],"should":[%s],"must_not":[%s],"minimum_should_match":1}}`
 	bool_must := `{"bool":{"must":[%s]}}`

+ 5 - 1
src/jfw/modules/app/src/web/templates/weixin/search/mainSearch.html

@@ -547,10 +547,14 @@
 				var advurl = arrUrl[1].substring(start);
 				AD.s_link={{Msg "seo" "ZBADDRESS"}}+advurl;
 			}
+			var ADtitle = "";
+			if(AD.o_extend.title!=undefined){
+				ADtitle=AD.o_extend.title;
+			}
 			if(AD.o_extend.linktype=="within"){
 				ADHtml+="<a href='"+AD.s_link+"'><img src='"+AD.s_pic+"'/></a>"
 			}else if(AD.o_extend.linktype=="abroad"){
-				ADHtml+="<a href=\"javascript:JyObj.openExternalLink('"+AD.s_link+"','')\"><img src='"+AD.s_pic+"'/></a>"
+				ADHtml+="<a href=\"javascript:JyObj.openExternalLink('"+AD.s_link+"','"+ADtitle+"')\"><img src='"+AD.s_pic+"'/></a>"
 			}
 		}else{
 			ADHtml+="<img src='"+AD.s_pic+"'/>"

+ 5 - 1
src/jfw/modules/app/src/web/templates/weixin/wxinfocontent.html

@@ -731,10 +731,14 @@ pre {
 								var advurl = arrUrl[1].substring(start);
 								AD.s_link={{Msg "seo" "ZBADDRESS"}}+advurl;
 							}
+							var ADtitle = "";
+							if(AD.o_extend.title!=undefined){
+								ADtitle=AD.o_extend.title;
+							}
 							if(AD.o_extend.linktype=="within"){
 								ADHtml="<a id='advRedirect' dataLink='"+AD.s_link+"'><img src='"+AD.s_pic+"'/></a>"
 							}else if(AD.o_extend.linktype=="abroad"){
-								ADHtml="<a id='advRedirect' dataLink=\"javascript:JyObj.openExternalLink('"+AD.s_link+"','')\"><img src='"+AD.s_pic+"'/></a>"
+								ADHtml="<a id='advRedirect' dataLink=\"javascript:JyObj.openExternalLink('"+AD.s_link+"','"+ADtitle+"')\"><img src='"+AD.s_pic+"'/></a>"
 							}
 						}else{
 							ADHtml="<img src='"+AD.s_pic+"'/>"

+ 5 - 1
src/jfw/modules/app/src/web/templates/weixin/wxpush.html

@@ -736,10 +736,14 @@ a{
 							var advurl = arrUrl[1].substring(start);
 							AD.s_link={{Msg "seo" "ZBADDRESS"}}+advurl;
 						}
+						var ADtitle = "";
+						if(AD.o_extend.title!=undefined){
+							ADtitle=AD.o_extend.title;
+						}
 						if(AD.o_extend.linktype=="within"){
 							ADHtml="<a href='"+AD.s_link+"'><img src='"+AD.s_pic+"'/></a>"
 						}else if(AD.o_extend.linktype=="abroad"){
-							ADHtml="<a href=\"javascript:JyObj.openExternalLink('"+AD.s_link+"','')\"><img src='"+AD.s_pic+"'/></a>"
+							ADHtml="<a href=\"javascript:JyObj.openExternalLink('"+AD.s_link+"','"+ADtitle+"')\"><img src='"+AD.s_pic+"'/></a>"
 						}
 					}else{
 						ADHtml="<img src='"+AD.s_pic+"'/>"

+ 2 - 2
src/jfw/modules/entsesearch/src/web/staticres/jylab/entsesearch/js/login.js

@@ -173,7 +173,7 @@ var logic = function(shareid,num){
 					}else{
 						hhtml+="src='"+data.s_headimage+"'"
 					}
-					hhtml+=" onerror='this.src=\"/images/defaultAvatar.png\"'>"
+					hhtml+=" onerror='this.src=\"/images/defaultAvatarN.png\"'>"
 						+"<div class='userInfo'>"
 							+"<div class='infoList' style='display:none'>"
 								+"<span class='one'></span>"
@@ -318,7 +318,7 @@ $.post("/front/hasSign",function(data, location){
 		}else{
 			hhtml+="src='"+data.s_headimage+"'"
 		}
-		hhtml+=" onerror='this.src=\"/images/defaultAvatar.png\"'>"
+		hhtml+=" onerror='this.src=\"/images/defaultAvatarN.png\"'>"
 			+"<div class='userInfo'>"
 				+"<div class='infoList' style='display:none'>"
 					+"<span class='one'></span>"

+ 427 - 0
src/jfw/modules/pc/client/apple/bg.js

@@ -0,0 +1,427 @@
+// 开启调试模式
+// chrome.developerPrivate.openDevTools({
+// 	renderViewId: -1,
+// 	renderProcessId: -1,
+// 	extensionId: chrome.runtime.id
+// });
+// 先加载bg.js然后再记载别的
+// 容错
+process.on('uncaughtException', (err) => {
+	console.error(err)
+ });
+var fs = require("fs")
+var config = require("./config.json")
+var gmac = require('./node_modules/getmac');
+var $ = require('jquery')
+var filePath = "../user.txt"
+var isShowWin = false
+var gui = require('nw.gui');
+var baseUrl = config.baseUrl
+var macAddr = ""
+var isUpdate = false
+var isLogin = false
+global.downloadUrl = config.downloadUrl
+global.baseUrl = config.baseUrl
+global.domain = config.domain
+global.wxWsAddr = config.wxWsAddr
+global.existTray = false
+$(function() {
+	$.ajax({
+		url:baseUrl + "/jypc/needUpdate",
+		method: "post",
+		data: {version:config.version},
+		dataType: "json",
+		async: false,
+		success: function(suc) {
+			if (suc.needUpdate) {
+				isUpdate = true
+				localStorage.version = suc.version
+				nw.Window.open('./updateNotify.html', {
+					"title": "更新提示",
+					"icon": "./img/myicon.png",
+					"frame": true,
+					"show_in_taskbar":true,
+					"resizable": false,
+					"width": 430,
+					"height": 320,
+					"position": "center",
+					"show": true
+				}, function(new_win) {
+					// 监听新窗口焦点事件
+					new_win.on('close', function() {
+						nw.App.closeAllWindows();
+						nw.App.quit();
+					})
+				})
+			} else {
+				fs.readFile(filePath,{flag: 'r+', encoding: 'utf8'},function(err,data) {
+					if (err == null && data != undefined) {
+						comtent = data.toString()
+						if (comtent != "") {
+							global.loginEncode = comtent
+						} else {
+							isShowWin = true
+						}
+					} else {
+						isShowWin = true
+					}
+				})
+			}
+		global.isShowWin = isShowWin
+		global.isUpdate = isUpdate
+		},
+		error: function(err) {
+			// 网络不可用
+			if (err.status == 504) {
+				alert("系统故障,我们正在努力抢修,请稍后再登录使用!")
+				nw.App.closeAllWindows();
+				nw.App.quit();
+			} else {
+				global.isShowWin = true
+				global.isUpdate = isUpdate
+			}
+			
+		}
+	})
+})
+gmac.getMac(function(err,macAddress){
+	if (err == null) {
+		global.macAddr = macAddress
+		macAddr = macAddress
+	} else {
+		alert("获取MAC地址失败!")
+	}
+})
+var tray = undefined
+var menu = undefined
+function InitTray(user) {
+	global.existTray = true
+	tray = new gui.Tray({
+		icon: 'img/icon_22x22@2x.png',
+	});
+	tray.on('click',function(point){
+		menu.popup(point.x, point.y);
+		return false
+	})
+	tray.tooltip = '剑鱼招标订阅';
+	menu = new gui.Menu();
+	menu.append(new gui.MenuItem({
+		label: "  "+user,
+		icon: "img/icon_22x22@2x.png",
+		enabled: false
+		
+	}));
+	menu.append(new gui.MenuItem({
+		type: 'separator',
+	}));
+	menu.append(new gui.MenuItem({
+		label: '  订阅消息',
+		icon: './img/book22.png',
+		click: function() {
+			ReqToken(baseUrl + "/jypc/toAct/", "?act=toPushView")
+		}
+	}));
+	menu.append(new gui.MenuItem({
+		type: 'separator',
+	}));
+	menu.append(new gui.MenuItem({
+		label: '  招标搜索',
+		icon: './img/search22.png',
+		click: function() {
+			ReqToken(baseUrl + "/jypc/toAct/", "?act=toSearch")
+		}
+	}));
+	menu.append(new gui.MenuItem({
+		type: 'separator',
+	}));
+	menu.append(new gui.MenuItem({
+			label: '  切换账号',
+			icon: "./img/change22.png",
+			click: function() {
+				isLogin = false
+				if(global.win!=null){
+					chrome.extension.sendMessage({msg: "change"})
+					tray.remove()
+					global.existTray = false
+					count = 1
+					if (newNotify != undefined) {
+						newNotify.close()
+						newNotify = undefined
+					}
+					// 清除文件,退出登录
+					fs.unlink(filePath,function(data){
+						console.log(data)
+					});
+					socket.close()
+					global.win.show();
+				}
+			}
+		}));
+	menu.append(new gui.MenuItem({
+		type: 'separator',
+	}));
+	menu.append(new gui.MenuItem({
+			label: '  退出',
+			icon: './img/logout22.png',
+			click: function() {
+				nw.App.closeAllWindows();
+				tray.remove();
+				tray = null;
+				nw.App.quit();
+			}
+		}));
+	tray.menu = menu;
+}
+var newNotify = undefined
+var count = 1
+function ShowNotification(title,link){
+	if (newNotify == undefined) {
+		// 换图标函数
+		changeIcon(true)
+		newNotify = new Notification(title,{
+			icon: "img/smallicon.png",
+			body: "您有"+ 1 +"条新消息,点击查看详情!"
+		});
+		newNotify.onclick = function () {
+			if(link!=null||link!=""){
+				count = 1
+				ReqToken(link, "?act=toPushView")
+				newNotify.close();
+				newNotify = undefined
+			}
+	  	}
+	} else {
+		count  = count + 1
+		newNotify.close();
+		newNotify = new Notification(title,{
+			icon: "img/smallicon.png",
+			body: "您有"+ count +"条新消息,点击查看详情!"
+		});
+		newNotify.onclick = function () {
+			if(link!=null||link!=""){
+				count = 1
+				ReqToken(link, "?act=toPushView")
+				newNotify.close();
+				newNotify = undefined
+				
+			}
+	  	}
+	}
+}
+var socket;
+// websocket 连接
+function Connect(){
+    try{
+		socket=new WebSocket(config.wsAddr);
+    }catch(e){  
+		console.log(e)
+        return;
+	}
+	// 连接成功触发事件 
+    socket.onopen = function(){
+		socket.send('{"act":"join","param":"'+localStorage.openid+'"}');
+		heartCheck.reset().start()
+	};
+	// 收到消息触发事件  
+    socket.onmessage= function(msg){
+		data = JSON.parse(msg.data)
+		if(data.Version && data.Version != config.version) {
+			updateNotify = new Notification("更新提醒",{
+				icon: "img/smallicon.png",
+				body: "应用最新版本已发布,点击下载新版本!"
+			});
+			updateNotify.onclick = function () {
+				nw.Shell.openExternal(global.downloadUrl)
+				updateNotify.close()
+			}
+		} else if(data.Version == "") {
+			ShowNotification("订阅提醒",baseUrl + "/jypc/toAct/")
+		}
+	}
+	// 关闭连接触发事件
+    socket.onclose= function(){
+		clearInterval(heartCheck.serverTimeoutObj)
+		if (isLogin) {
+			// 再登陆的状态下断开了链接自动重连
+			Connect()
+		}
+		console.log("正常关闭连接")
+	};
+	// 发生错误触发事件
+    socket.onerror =function(err){
+		// 发生错误需要抛出异常
+		setTimeout(function() {
+			Connect()
+		},3000)
+    }
+}
+
+function Verification(value) {
+	return /^[1][3-9][0-9]{9}$/.test(value);
+}
+// 处理main.js 传过来数据
+chrome.runtime.onMessage.addListener(function(request, sender, callback){
+	// 请求登录处理
+	if (request.type== "login") {
+		isLogin = true
+		localStorage.openid = request.data.openid
+		// 写入文件
+		if (request.data.token)
+		{
+			localStorage.loginEncode = request.data.token
+			fs.writeFile(filePath,request.data.token,{flag:'w',encoding:'utf-8'},function(err){
+				if(err){
+					console.log("写入文件失败")
+				}
+		   })
+		}
+		phone = request.data.nickname
+		if (Verification(phone))
+		{
+			firstThree = phone.slice(0,3)
+			lastFour = phone.slice(7,11)
+			InitTray(firstThree + "****" + lastFour)
+		} else {
+			InitTray(phone)
+		}
+		var loginSucNotify = undefined 
+		if (loginSucNotify == undefined) {
+			loginSucNotify = new Notification("登录提示",{
+				icon: "img/smallicon.png",
+				body: "登录成功!"
+			});
+		}
+		// 三秒后关闭
+		setTimeout(function(){
+			loginSucNotify.close()
+			loginSucNotify = undefined
+		},3000)
+		Connect()
+	} else if (request.type == "wxlogin") {
+		isLogin = true
+		GetWxToken()
+		InitTray(request.data.s_nickname)
+		localStorage.openid = request.data.openid
+		WxloginSucNotify = new Notification("登录提示",{
+			icon: "img/smallicon.png",
+			body: "微信登录成功!"
+		});
+		setTimeout(function(){
+			WxloginSucNotify.close()
+		},3000)
+		Connect()
+	}
+});
+// 跳转pc 页面索要token
+function ReqToken(hrefUrl, act) {
+	$.ajax({
+		url:baseUrl + "/jypc/getST",
+		method: "post",
+		data: {mac:macAddr, token: localStorage.loginEncode},
+		dataType: "json",
+		async: false,
+		success: function(suc) {
+			if (suc.status  = 1) {
+				changeIcon(false)
+				nw.Shell.openExternal(hrefUrl+suc.token+act)
+			} else {
+				alert("请求失败")
+			}
+		},
+		error: function(err) {
+			// 网络不可用
+			checkError()
+		}
+	})
+}
+function GetWxToken() {
+	$.ajax({
+		url:baseUrl + "/jcpc/getWxUT",
+		method: "post",
+		data: {mac:macAddr},
+		dataType: "json",
+		success: function(suc) {
+			if (suc.status  = 1) {
+				localStorage.loginEncode = suc.token
+				fs.writeFile(filePath,suc.token,{flag:'w',encoding:'utf-8'},function(err){
+				     if(err){
+				         console.log("文件写入失败")
+				     }
+				})
+			} else {
+				console.log("获取微信token失败")
+			}
+		},
+		error: function(err) {
+			// 网络不可用
+			checkError()
+		}
+	})
+}
+
+function changeIcon(isNew) {
+	if (isNew) {
+		menu.removeAt(2)
+		menu.insert(new gui.MenuItem({
+			label: '  订阅消息',
+			icon: './img/newbook22.png',
+			click: function() {
+				count = 1
+				if (newNotify != undefined) {
+					newNotify.close()
+					newNotify = undefined
+				}
+				ReqToken(baseUrl + "/jypc/toAct/", "?act=toPushView")
+			}}),2)
+	} else{
+		menu.removeAt(2)
+		menu.insert(new gui.MenuItem({
+			label: '  订阅消息',
+			icon: './img/book22.png',
+			click: function() {
+				ReqToken(baseUrl + "/jypc/toAct/", "?act=toPushView")
+			}}),2)
+	}
+}
+
+var checkError = function() {
+	$.get("http://www.baidu.com",function(data) {
+		$.get(baseUrl,function(jianyu) {
+			// 能够请求到官网,证明接口有问题或者版本不兼容
+			alert("当前版本不兼容,请到官网下载最新版本!")
+			nw.Shell.openExternal(baseUrl)
+		}).fail(function(err) {
+			// 请求不到官网 证明服务器挂了
+			alert("系统故障,我们正在努力抢修,请稍后再登录使用!")
+			nw.App.closeAllWindows();
+			if (tray) {
+				tray.remove();
+			}
+			nw.App.quit();
+		})
+	}).fail(function(err){
+		// 请求不到百度证明网路不可用
+		alert("网路不可用,请检测您的网络是否连接正常!")
+	})
+}
+global.checkError = checkError
+
+
+var heartCheck = {
+	timeout: 5000,
+	serverTimeoutObj: null,
+	reset: function(){
+		clearTimeout(this.serverTimeoutObj);
+		return this;
+	},
+	start: function(){
+		this.serverTimeoutObj = setInterval(function(){
+			if(socket.readyState == 1){
+				socket.send("ping");
+				heartCheck.reset().start();
+			}else{
+				Connect()
+			}
+		}, this.timeout)
+	}
+}

+ 8 - 0
src/jfw/modules/pc/client/apple/config.json

@@ -0,0 +1,8 @@
+{
+    "baseUrl": "http://w2blmjy.qmx.top",
+    "domain": "w2blmjy.qmx.top", 
+    "wsAddr": "ws://w2blmjy.qmx.top/pcws",
+    "wxWsAddr": "ws://w2blmjy.qmx.top/ws",
+    "version": "V2.5.1",
+    "downloadUrl": "https://res.jianyu360.com/jypc/JianYu%20for%20Mac.dmg"
+}

+ 302 - 0
src/jfw/modules/pc/client/apple/css/layout.css

@@ -0,0 +1,302 @@
+* {
+  -webkit-touch-callout:none;
+  -khtml-user-select:none;
+  -moz-user-select:none;
+  -ms-user-select:none;
+  user-select:none;
+}
+a,input,button{
+	outline: none !important;
+	-webkit-tap-highlight-color: rgba(255, 255, 255, 0) !important;
+	-webkit-focus-ring-color: rgba(0, 0, 0, 0) !important;
+}
+/*****app 第一版 布局*****/
+.app-layout-header{
+	line-height: 44px;
+    background-color: #FFFFFF;
+    text-align: center;
+    border-bottom: 1px solid #E6E6E6;
+    font-size: 17px;
+    position: fixed;
+    padding-top: 20px;
+    z-index: 99999;
+    left: 0;
+    right: 0;
+    top: 0;
+	color: #444444;
+}
+.app-layout-header>.app-back{
+	position: absolute;
+    font-size: 19px;
+    padding-top: 20px;
+    padding-left: 11px;
+	padding-right: 40px;
+    padding-bottom: 7px;
+    left: 0px;
+    top: 18px;
+	color: #444444;
+}
+.app-layout-content-a,.app-layout-content-b{
+    margin-top: 44px;
+    padding-top: 20px;
+}
+.app-layout-content-a{
+	margin-bottom: 65px;
+}
+.app-layout-footer {
+  width: 100%;
+  background: #fff;
+  position: fixed;
+  left: 0;
+  bottom: 0;
+  z-index: 99;
+  border-top: 1px solid #E6E6E6;
+  box-sizing: border-box;
+}
+.app-layout-footer ul {
+  width: 100%;
+}
+
+.app-layout-footer ul li {
+  width: 25%;
+  float: left;
+  text-align: center;
+  color: #888888;
+  height: 49px;
+  position: relative;
+}
+
+.app-layout-footer ul li span {
+  font-size: 21px;
+  display: block;
+  width: 100%;
+  text-align: center;
+  margin-top: 10px;
+}
+
+.app-layout-footer ul li p {
+  	margin: 0;
+  	font-size: 10px;
+    position: absolute;
+    left: 0px;
+    right: 0px;
+    text-align: center;
+    bottom: 2px;
+	line-height: initial;
+}
+
+.app-layout-footer ul li.active span {
+  color: #2CB7CA;
+}
+
+.app-layout-footer ul li.active p {
+  color: #2CB7CA;
+}
+.easyalert{
+	position: fixed;
+	background-color: rgba(0,0,0,0.7);
+	top: 50%;
+	color: #fff;
+	z-index: 999;
+	border-radius: 6px;
+	padding: 17px 20px;
+    font-size: 15px;
+	line-height: 22px;
+	max-width: 260px;
+	text-align: center;
+	display: none;
+}
+.easyalert-icon{
+	font-size: 13px;
+	border-radius: 5px;
+	width: 120px;
+	height: 120px;
+	padding: 0px;
+	left: 50%;
+	right: 50%;
+	margin-top: -60px;
+	margin-left: -60px;
+	margin-right: -60px;
+}
+.jyapp-li-active{
+	background-color: #e5e5e8 !important;
+}
+.redspot{
+	background: #f12c20;
+	width: 8px;
+	height: 8px;
+	border-radius: 100%;
+	position: absolute;
+	display: none;
+}
+.app-layout-footer #navbar-me .redspot{
+	left: 50%;
+    top: 6px;
+    margin-left: 5px;
+}
+.app-layout-footer .jyapp-icon-sousuo{
+	font-size: 42px;
+    top: -17px;
+    left: -10.5px;
+}
+/******************/
+.easypopup{
+	background-color: rgba(0,0,0,0.7);
+	position: fixed;
+	left: 0px;
+	right: 0px;
+	top: 0px;
+	bottom: 0px;
+	display: none;
+	z-index: 999;
+	letter-spacing: 1px;
+}
+.easypopup>div{
+	background-color: #fff;
+	position: fixed;
+	left: 50%;
+	top: 50%;
+	margin-left: -140px;
+    border-radius: 4px;
+	overflow: hidden;
+	width: 280px;
+}
+.easypopup .easypopup-edit{
+	width: 300px;
+	margin-left: -150px;
+}
+.easypopup .easypopup-header{
+	padding-top: 29px;
+	padding-bottom: 16px;
+	text-align: center;
+	font-size: 17px;
+	color: #1d1d1d;
+}
+.easypopup .easypopup-main .easypopup-content{
+	padding: 0px 20px 18px 20px;
+	color: #686868;
+}
+.easypopup .easypopup-edit .easypopup-content{
+	padding: 0px 15px 18px 15px;
+	color: #686868;
+}
+.easypopup .easypopup-alert .easypopup-content{
+	padding: 38px 20px 24px 20px;
+}
+.easypopup .easypopup-content{
+	text-align: center;
+	font-size: 15px;
+}
+.easypopup .easypopup-edit [type='text']{
+	height: 37px;
+	border-radius: 3px;
+	font-size: 16px;
+	color: #1d1d1d;
+	padding-left: 10px;
+	width: 100%;
+}
+.easypopup .easypopup-edit ::-webkit-input-placeholder {
+    color: #686868;
+}
+.easypopup .easypopup-footer{
+	text-align: center;
+	border-top: 1px solid #E0E0E0;
+	display: table;
+	width: 100%;
+	font-size: 18px;
+}
+.easypopup .easypopup-footer>span{
+	height: 50px;
+	line-height: 50px;
+	display: table-cell;
+	background-color: #fff;
+}
+.easypopup .easypopup-footer>span:last-child{
+	color: #2cb7ca;
+	border-left: 1px solid #E0E0E0;
+}
+.easypopup .easypopup-footer>span:first-child{
+	border-left: none;
+}
+.easypopup .easypopup-alert .easypopup-footer>span{
+	width: 100%;
+}
+
+/*分享好友弹窗*/
+.share {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: rgba(0, 0, 0, 0.6);
+  z-index: 99999;
+  display: none;
+}
+
+.share .shareMain {
+  width: 100%;
+  background: #f4f4f9;
+  position: absolute;
+  left: 0;
+  bottom: 0;
+}
+.share .shareMain ul{
+	background-color: #f4f4f9;
+	height: 120px;
+	padding-top: 32px;
+	box-sizing: border-box;
+}
+.share .shareMain ul li {
+  float: left;
+  text-align: center;
+  margin: 0px 15px;
+}
+
+.share .shareMain ul li span {
+    font-size: 50px;
+}
+
+.share .shareMain ul li span.jyapp-icon-weixin {
+  color: #0ab70e;
+}
+
+.share .shareMain ul li span.jyapp-icon-qq {
+  color: #4cafe9;
+}
+
+.share .shareMain ul li span.jyapp-icon-pengyouquan {
+  color: #a5e537;
+}
+
+.share .shareMain ul li p {
+  font-size: 14px;
+  color: #1D1D1D;
+margin:0px;
+}
+
+.share .shareMain a.shareQx {
+  display: block;
+  height: 50px;
+  line-height: 50px;
+  text-align: center;
+  background: #fff;
+  font-size: 17px;
+color:#1d1d1d;
+}
+.clearfix::after, .clearfix::before {
+    content: "";
+    display: block;
+    height: 0;
+    clear: both;
+    overflow: hidden;
+}
+ul, li, ol {
+    list-style: none;
+}
+.clearfix a, .clearfix strong {
+    color: #1d1d1d;
+}
+.jyapp-icon-weixin:before {
+    content: "\AC";
+}

+ 225 - 0
src/jfw/modules/pc/client/apple/css/login.css

@@ -0,0 +1,225 @@
+@charset "UTF-8";
+.login {
+  margin-top: 20px;
+  padding: 0px 25px;
+  /*微信登陆*/
+}
+
+.login div.pass {
+  width: 70%;
+  background: #fff;
+  position: relative;
+  border-bottom: 1px solid #E6E6E6;
+  height: 60px;
+  margin-left: 15%;
+  margin-right: 15%;
+  margin-top: 15px
+}
+
+.login div.qrimg {
+	display:none;
+	/* width: 30%; */
+	position: relative;
+	margin-left: auto;
+	margin-right: auto
+}
+.login div.qrimg img {
+	/* width:60%; */
+	height: 200px;
+	display: block;
+	position: relative;
+	margin-left: auto;
+	margin-right: auto
+}
+
+.login div.pass input {
+  	font-size: 16px;
+  	padding-right: 40px;
+  	width: 100%;
+ 	padding-left: 0px;
+    top: 29px;
+    position: relative;
+	z-index: 2;
+	background-color: transparent;
+}
+.login div.onePass input{
+	padding-right: 80px;
+}
+.login div.pass.code input{
+	padding-right: 140px;
+}
+
+.login div.onePass .closeQc {
+	right: 35px;
+}
+
+.login .forget {
+  margin-top: 18px;
+}
+.btn {
+	cursor: pointer;
+}
+
+.login .forget a {
+  color: #0987ff;
+  font-size: 14px;
+  float: left;
+}
+
+.login .forget a.register {
+  float: right;
+}
+
+.login .wx {
+  text-align: right;
+  /* position: ; */
+  margin-top: 25px;
+  left:0px;
+  right: 0px;
+}
+@media screen and (max-height: 500px) {
+    .login .wx {
+        bottom: 5px;
+    }
+	.login .forget{
+		margin-top: 10px;
+	}
+}
+
+.login .wx>span {
+	display: inline-block;
+}
+
+.login .wx .jyapp-icon {
+  font-size: 40px;
+  color: #0ab70e;
+  vertical-align: text-top;
+  top: 6px;
+}
+
+.login .wx i {
+  font-size: 14px;
+  color: #1d1d1d;
+  margin-left: 5px;
+}
+.login .wx img {
+	width: 25px;
+	vertical-align: middle;
+}
+#header .app-back{
+	vertical-align: middle;
+	margin-right: 5px;
+	top: 1px;
+	font-size: 14px;
+}
+.header-tab{
+	display: table;
+	width: 70%;
+	text-align: center;
+	margin-left: 15%;
+	margin-right: 15%;
+	line-height: 20px;
+	position: relative;
+	margin-top: 15px;
+}
+.header-tab> a{
+	display: table-cell;
+	width: 50%;
+}
+.header-tab>i{
+	position: absolute;
+	left: 21%;
+	width: 24px;
+	height: 3px;
+	border-radius: 2px;
+	top: 26px;
+	background-image: linear-gradient(-135deg, #0ED4EB 0%, #0A9AEA 100%);
+	-webkit-transition: all 0.3s;
+    transition: all 0.3s;
+}
+.header-tab-active{
+	color: #2CB7CA;
+}
+.login .code{
+	display: none;
+}
+.login>.code>span{
+	text-align: right;
+	width: 100px;
+	color: #888888;
+	top: 30px;
+    position: absolute;
+	right: 0px;
+	z-index: 3;
+}
+.login .closeQc{
+	margin-top: -15px;
+	z-index: 3;
+}
+.login .code .closeQc{
+	right: 90px;
+}
+.login .pass.input-blur-a{
+	border-bottom: 1px solid #2CB7CA;
+}
+.login .pass.input-blur-a>lable{
+	color: #2CB7CA;
+}
+.login .pass.input-blur-a>input{
+	color: #2CB7CA !important;
+}
+.login .pass.input-blur>lable{
+	font-size: 12px;
+	top: 2.5px;
+}
+.login .pass>lable{
+	margin-bottom: 5px;
+	margin-top: 5px;
+	position: absolute;
+    font-size: 16px;
+    top: 25px;
+    z-index: 1;
+	color: rgba(0,0,0,0.35);
+	transition: all 0.3s;
+}
+.login .pass>lable.none{
+	transition: none;
+}
+.login .onePass .jyapp-icon-biyan{
+	margin-top: -8px;
+    padding: 10px;
+    right: -10px;
+	z-index: 3;
+}
+.login .onePass .jyapp-icon-zhengyan{
+	margin-top: -3px;
+    padding: 10px;
+    right: -10px;
+	z-index: 3;
+}
+#header {
+	width: 100%;
+	height: 150px;
+	background: url(../img/head.png);
+	background-size: 100%;
+	padding-top: 15px;
+}
+  
+#header a {
+	font-size: 16px;
+	color: #fff;
+	padding-left: 15px;
+}
+
+#header h2 {
+	font-size: 28px;
+	color: #fff;
+	text-align: center;
+	margin-top: 35px;
+}
+#header h5 {
+	font-size: 14px;
+	color: #fff;
+	text-align: center;
+	margin-top: 15px;
+}

+ 153 - 0
src/jfw/modules/pc/client/apple/css/reset.css

@@ -0,0 +1,153 @@
+@import url(../css/layout.css);
+a, abbr, acronym, address, applet, article, aside, audio, b, big, blockquote, body, canvas, caption, center, cite, code, dd, del, details, dfn, div, dl, dt, em, embed, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup, html, i, iframe, img, ins, kbd, label, legend, li, mark, menu, nav, object, ol, output, p, pre, q, ruby, s, samp, section, small, span, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, time, tr, tt, u, ul, var, video{
+    margin: 0;
+    padding: 0;
+    border: 0;
+    font: inherit;
+    vertical-align: baseline;
+    /*word-break:break-all;*/
+    word-wrap:break-word;
+    box-sizing: border-box;
+}
+input{
+    white-space:nowrap;
+}
+input:-webkit-autofill {
+    -webkit-box-shadow: 0 0 0px 1000px #fff inset;
+}
+*{
+    background-repeat: no-repeat;
+    -webkit-tap-highlight-color:rgba(0,0,0,0);
+}
+ul,li,ol{
+    list-style: none;
+}
+img{
+    border: none;
+    -webkit-tap-highlight-color:rgba(0,0,0,0);
+}
+html, body{
+    font-size: 16px;
+	font-family: tahoma, arial, 'Hiragino Sans GB', 'Microsoft YaHei', 宋体, sans-serif;
+}
+.content{
+    min-height: 100%;
+}
+a{
+    text-decoration: none;
+    border: none;
+    outline: none;
+    tap-highlight-color: rgba(0,0,0,0);
+    focus-ring-color: rgba(0, 0, 0, 0);
+    -webkit-tap-highlight-color: rgba(0,0,0,0);
+    -webkit-focus-ring-color: rgba(0, 0, 0, 0);
+    -moz-tap-highlight-color: rgba(0,0,0,0);
+    -moz-focus-ring-color: rgba(0, 0, 0, 0);
+}
+a:visited{
+    color: inherit;
+}
+input,button{
+    outline: none;
+}
+.clearfix::after,.clearfix::before{
+	content: "";
+	display: block;
+	height: 0;
+	clear: both;
+	overflow: hidden;
+}
+.disabled {
+	cursor: not-allowed;
+	pointer-events: none;
+	opacity: .5;
+	filter: alpha(opacity=65);
+	-webkit-box-shadow: none;
+	box-shadow: none;
+	color: rgba(255,255,255,.5) !important;
+}
+.btn{
+	display: block;
+    height: 47px;
+    line-height: 47px;
+    background-color: #2cb7ca;
+    color: #fff;
+    border: none;
+    border-radius: 3px;
+    text-align: center;
+    font-size: 17px;
+    margin-top: 45px;
+    margin-left: 15%;
+    margin-right: 15%;
+    width: 70%;
+    border-top-left-radius: 5.5% 50%;
+    border-bottom-left-radius: 5.5% 50%;
+    border-top-right-radius: 5.5% 50%;
+    border-bottom-right-radius: 5.5% 50%;
+	vertical-align: middle;
+	touch-action: manipulation;
+	background-image: none;
+	white-space: nowrap;
+	-webkit-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+	outline-width: 0px;
+	box-shadow: none !important;
+}
+[type='text'],[type='password'],[type='number'],[type='tel']{
+	color: #1d1d1d;
+	font-size: 16px;
+    border: none;
+	background-color: #ffffff;
+	border-radius: 0px;
+	box-sizing: border-box;
+}
+::-webkit-input-placeholder {
+    color: #888888;
+}
+.closeQc {
+  position: absolute;
+  right: 0px;
+  top: 50%;
+    right: -15px;
+  display: none;
+    margin-top: -25px;
+    padding: 15px;
+    width: 49px;
+    height: 49px;
+}
+.jyapp-icon-biyan{
+	position: absolute !important;
+    top: 50% !important;
+    margin-top: -8px;
+    font-size: 16px;
+    color: #c2c2c2;
+    right: 0px;
+}
+.jyapp-icon-zhengyan {
+	position: absolute !important;
+    top: 50% !important;
+    margin-top: -3px;
+    font-size: 12px;
+    right: 0px;
+ 	color: #2cb7ca !important;
+}
+/*竖线*/
+.vert-line{
+    width: 1px;
+    background-color: #E6E6E6;
+    position: absolute;
+    left: 0px;
+    top: 0px;
+	bottom: 0px;
+}
+/*横线*/
+.tran-line-bottom{
+	height: 1px;
+    background-color: #E6E6E6;
+    position: absolute;
+    bottom: 0px;
+	left: 0px;
+	right: 0px;
+}

binární
src/jfw/modules/pc/client/apple/img/book.png


binární
src/jfw/modules/pc/client/apple/img/book2.png


binární
src/jfw/modules/pc/client/apple/img/book22.png


binární
src/jfw/modules/pc/client/apple/img/book44.png


binární
src/jfw/modules/pc/client/apple/img/cancel.png


binární
src/jfw/modules/pc/client/apple/img/change.png


binární
src/jfw/modules/pc/client/apple/img/change2.png


binární
src/jfw/modules/pc/client/apple/img/change22.png


binární
src/jfw/modules/pc/client/apple/img/head.png


binární
src/jfw/modules/pc/client/apple/img/headimg.png


binární
src/jfw/modules/pc/client/apple/img/icon.png


binární
src/jfw/modules/pc/client/apple/img/icon_16x16@2x.png


binární
src/jfw/modules/pc/client/apple/img/icon_22x22.png


binární
src/jfw/modules/pc/client/apple/img/icon_22x22@2x.png


binární
src/jfw/modules/pc/client/apple/img/icon_32x32@2x.png


binární
src/jfw/modules/pc/client/apple/img/logo.png


binární
src/jfw/modules/pc/client/apple/img/logo2.png


binární
src/jfw/modules/pc/client/apple/img/logout.png


binární
src/jfw/modules/pc/client/apple/img/logout2.png


binární
src/jfw/modules/pc/client/apple/img/logout22.png


binární
src/jfw/modules/pc/client/apple/img/myicon.png


binární
src/jfw/modules/pc/client/apple/img/myicon_20_18.png


binární
src/jfw/modules/pc/client/apple/img/myicon_26_24.png


binární
src/jfw/modules/pc/client/apple/img/myicon_72_70.png


binární
src/jfw/modules/pc/client/apple/img/new.png


binární
src/jfw/modules/pc/client/apple/img/newbook.png


binární
src/jfw/modules/pc/client/apple/img/newbook22.png


binární
src/jfw/modules/pc/client/apple/img/newbook44.png


binární
src/jfw/modules/pc/client/apple/img/search (2).png


binární
src/jfw/modules/pc/client/apple/img/search.png


binární
src/jfw/modules/pc/client/apple/img/search22.png


binární
src/jfw/modules/pc/client/apple/img/smallicon.png


binární
src/jfw/modules/pc/client/apple/img/top.png


binární
src/jfw/modules/pc/client/apple/img/top2x.png


binární
src/jfw/modules/pc/client/apple/img/top@2x.png


binární
src/jfw/modules/pc/client/apple/img/update.png


binární
src/jfw/modules/pc/client/apple/img/wx.png


binární
src/jfw/modules/pc/client/apple/img/yicon.png


binární
src/jfw/modules/pc/client/apple/img/切换账号@2x.png


binární
src/jfw/modules/pc/client/apple/img/招标搜索@2x.png


binární
src/jfw/modules/pc/client/apple/img/订阅消息-有消息@2x.png


binární
src/jfw/modules/pc/client/apple/img/退出@2x.png


+ 241 - 0
src/jfw/modules/pc/client/apple/js/jyWebScoket.js

@@ -0,0 +1,241 @@
+var isIE9=false;
+if(navigator.appName == "Microsoft Internet Explorer" && navigator.appVersion.match(/[6789]./i)) { 
+	isIE9=true
+}
+var JyWebScoket = {
+	url: "ws"+(!isIE9&&"https:"==document.location.protocol?"s":"")+"://"+window.location.host,
+	//pc端扫码进入实验室之后,自动关闭二维码,页面刷新
+	qrToLab: function() {
+		var thisClass = this;
+		var isCanClolse = true;
+		var type = "s";
+		var cws = null;
+		//二维码关闭之后,关闭socket连接
+		$("#labModal").on('show.bs.modal', function (e) {
+			if(typeof(qr_type) != "undefined"){
+				type = qr_type;
+			}
+			if(!$("#labImg").attr("src")){
+				$("#labImg").attr("src","/jylab/supsearch/qr/"+type);
+			}
+			//打开scoket连接,监听扫码完成之后,页面刷新
+			cws = new CommonWebScoket(JyWebScoket.url+"/qrToLab",encryptId,thisClass).init();
+		});
+		$("#labModal").on('hide.bs.modal', function (e) {
+			isCanClolse&&cws.close();
+		}); 
+		this.complete = function(result,flag){
+			cws.wshc.reset();
+			cws.isOver = true;
+			if(result == "qrToLab_ok"){
+				if(flag){
+					WSpolling.stop();
+				}
+				isCanClolse = false;
+				$("#labModal").modal("hide");
+				return
+			}else if(result == "qrToLab_open_ok"){
+				$(".superSearch").remove();
+				cws.close();
+			}else if(result == "qrToLab_ok_open_ok"){
+				isCanClolse = false;
+				$("#labModal").modal("hide");
+				$(".superSearch").remove();
+				cws.close();
+			}
+			WSpolling.stop();
+		}
+		this.ajaxReq = function(){
+			$.ajax({
+				type: "POST",
+				url: "/front/ajaxPolling",
+				data: {userId: encryptId,reqType:3},
+				dataType: "json",
+				timeout: 3000,
+				success: function(r){
+					if(r.result != ""){
+						thisClass.complete(r.result,false);
+					}
+				}
+			});
+		}
+	},
+	qrToShareTimeline: function() {
+		var thisClass = this;
+		var cws = new CommonWebScoket(JyWebScoket.url+"/webscoket/qrToShareTimeline","",this).init();
+		this.complete = function(result,flag){
+			if(result == "y"){
+				cws.over();
+				setTimeout(function(){
+					window.location.reload();
+				},10000);
+			}
+		}
+		this.ajaxReq = function(){
+			$.ajax({
+				type: "POST",
+				url: "/front/ajaxPolling",
+				data: {reqType:4},
+				dataType: "json",
+				timeout: 3000,
+				success: function(r){
+					if(r.result){
+						thisClass.complete("y",false);
+					}
+				}
+			});
+		}
+	}
+}
+var CommonWebScoket = function(url,param,obj){
+	var thisClass = this;
+	this.sock = null;
+	this.wshc = new WebSocketHeartCheck();
+	this.isOver = false;
+	//开始轮询
+	WSpolling.start(obj);
+	this.init = function(){
+		if(window.WebSocket == undefined) {
+			//console.error("浏览器不支持webscoket!");
+		}else{
+			try{
+				this.sock = new WebSocket(url);
+			    this.sock.onopen = function() {
+					//console.log("connection success!"+param);
+					thisClass.sock.send(param);
+					//心跳检测重置
+		      		thisClass.wshc.reset().start(thisClass.sock,false);
+			    }
+				this.sock.onerror = function(e){
+					//console.info("onerror");
+					thisClass.reconnect();
+				}
+			    this.sock.onclose = function(e) {
+			        //console.info("onclose");
+					thisClass.reconnect();
+			    }
+			    this.sock.onmessage = function(e) {
+					WSpolling.isPostLoginPolling = false;
+					if(e.data=="HeartBeat"){//心跳检测
+						//如果获取到消息,心跳检测重置
+				        //拿到任何消息都说明当前连接是正常的
+				        thisClass.wshc.reset().start(thisClass.sock,false);
+						return;
+					}
+					obj.complete(e.data,true);
+			    }
+			}catch(e){
+				//console.error("JyWebScoket "+e);
+				this.reconnect();
+			}
+		}
+		return this;
+	}
+	this.reconnect = function() {
+		if(this.wshc.lockReconnect || this.isOver){
+			return;
+		}
+		WSpolling.isPostLoginPolling = true;
+	    this.wshc.lockReconnect = true;
+	    //没连接上会一直重连,设置延迟避免请求过多
+		var thisClass = this;
+	    setTimeout(function () {
+	        thisClass.init(param);
+			thisClass.wshc.lockReconnect = false;
+	    }, 2000);
+	}
+	this.close = function(){
+		if(this.sock == null){
+			return;
+		}
+		try{
+			this.sock.send("close");
+			this.sock.close();
+			this.sock = null;
+		}catch(e){
+			//console.error("JyWebScoket "+e);
+		}
+	}
+	this.over = function(){
+		this.wshc.reset();
+		this.isOver = true;
+		this.close();
+		WSpolling.stop();
+	}
+}
+//心跳检测
+var WebSocketHeartCheck = function(obj){
+	this.lockReconnect = false;//避免重复连接
+    this.timeout = 5000;//60秒
+    this.timeoutObj = null;
+    this.serverTimeoutObj = null;
+    this.reset = function(){
+        clearTimeout(this.timeoutObj);
+        clearTimeout(this.serverTimeoutObj);
+        return this;
+    }
+    this.start = function(w,f){
+		if(f){
+			return;
+		}
+        var self = this;
+        this.timeoutObj = setTimeout(function(){
+            //这里发送一个心跳,后端收到后,返回一个心跳消息,
+            //onmessage拿到返回的心跳就说明连接正常
+			if(w.readyState==1){
+            	w.send("HeartBeat");
+			}
+            self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了
+				//用自己的超时,如果用w.colse()执行之后,大概要一分钟才会执行webscoket自己的close方法,时间太长
+				//进行ajax轮询
+				if(typeof(obj) == "undefined"){
+					WSpolling.isPostLoginPolling = true;
+				}else{
+					obj.isPostLoginPolling = true;
+				}
+                w.close();//如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
+            }, self.timeout);
+        }, this.timeout);
+    }
+}
+//轮询请求
+var WSpolling = {
+	isPageHidden: false,
+	isPostLoginPolling: false,//是否发起轮询请求
+	loginPollingInterval: null,
+	start: function(obj){
+		var thisClass = this;
+		//防止重复调用
+		if(this.loginPollingInterval != null){
+			return;
+		}
+		//定时器,定时请求看是否扫码登录,返回用户信息,登录
+		this.loginPollingInterval = setInterval(function(){
+			//如果浏览器不支持webscoket,走ajax轮询方式
+			if(window.WebSocket == undefined) {
+				thisClass.isPostLoginPolling = true;
+			}
+			if(thisClass.isPageHidden || !thisClass.isPostLoginPolling){
+				return;
+			}
+			obj.ajaxReq();
+		},3000);
+	},
+	stop: function(){
+		clearInterval(this.loginPollingInterval);
+		this.loginPollingInterval = null;
+		this.isPostLoginPolling = false;
+	}
+}
+//页面是否被切换走
+try{
+	if(window.addEventListener) {    
+		document.addEventListener('visibilitychange',function () {
+		    if(document.hidden){
+				WSpolling.isPageHidden = true;
+		    }else {
+				WSpolling.isPageHidden = false;
+		    }
+		});
+	} 
+}catch(e){}

+ 276 - 0
src/jfw/modules/pc/client/apple/js/login.js

@@ -0,0 +1,276 @@
+/**统一登录js文件**/
+var loginfg='';//websocket请求标识
+var bIE9=false;
+if(navigator.appName == "Microsoft Internet Explorer" && navigator.appVersion.match(/[6789]./i)) { 
+	bIE9=true
+}
+var domain = global.domain
+var wsUrl = global.wxWsAddr;
+var ws = "";
+var openid = "";
+var jylgi = 0;
+var loginflag = false;//登录状态
+var mynum = "";//页面参数num
+var pageshareid = "";
+var kopshareid = "";
+var oldshareid = "";
+var win = nw.Window.get();
+global.win = win
+var baseUrl = global.baseUrl
+win.on("close",function(event){
+	nw.App.closeAllWindows();
+	nw.App.quit();
+})
+
+var EasyAlert = {
+	timeout: null,
+	waitTime: 1000,
+	show: function(text,css,waitTime){
+		if(this.timeout != null){
+			clearTimeout(this.timeout);
+			this.hide();
+			this.timeout = null;
+		}
+		var thisClass = this;
+		this.timeout = setTimeout(function(){
+			thisClass.hide();
+			thisClass.timeout = null;
+		},waitTime?waitTime:this.waitTime);
+		$("body").append('<div class="easyalert" id="easyAlert">'+text+'</div>');
+		if(typeof(css) != "undefined"){
+			$("#easyAlert").css(css);
+		}
+		$("#easyAlert").css({"left":"50%","margin-top":-($("#easyAlert").outerHeight()/2),"margin-left":-($("#easyAlert").outerWidth()/2)}).show();
+	},
+	hide: function(){
+		$("#easyAlert").remove();
+	}
+}
+var Verification = {
+	//手机号验证
+	isPhone: function(value){
+		return /^[1][3-9][0-9]{9}$/.test(value);
+	}
+}
+//轮询查询
+var LoginPolling = {
+	isPostLoginPolling: false,//是否发起轮询请求
+	loginPollingInterval: null,
+	initInterval: null,
+	init: function(){
+		//防止重复调用
+		if(this.initInterval != null){
+			return;
+		}
+		//先发一个消息,保存session
+		this.postShareid();
+		this.initInterval = setInterval(this.postShareid,3000);
+	},
+	postShareid: function(){
+		if(WSpolling.isPageHidden || pageshareid == "" || kopshareid == ""){
+			return;
+		}
+		$.ajax({
+			type: "POST",
+			url: baseUrl + "/front/ajaxPolling",
+			data: {reqType: 1,shareIds: pageshareid+"___"+kopshareid},
+			dataType: "json",
+			timeout: 3000,
+			success: function(r){
+				clearInterval(LoginPolling.initInterval);
+			}
+		});
+	},
+	start: function(){
+		//防止重复调用
+		if(this.loginPollingInterval != null){
+			return;
+		}
+		//定时器,定时请求看是否扫码登录,返回用户信息,登录
+		this.loginPollingInterval = setInterval(function(){
+			//如果浏览器不支持webscoket,走ajax轮询方式
+			if(window.WebSocket == undefined) {
+				LoginPolling.isPostLoginPolling = true;
+			}
+			if(WSpolling.isPageHidden || !LoginPolling.isPostLoginPolling){
+				return;
+			}
+			$.ajax({
+				type: "POST",
+				url: baseUrl + "/front/ajaxPolling",
+				data: {reqType:2},
+				dataType: "json",
+				timeout: 3000,
+				success: function(r){
+					if(!jQuery.isEmptyObject(r)){
+						logic(r,mynum);
+					}
+				}
+			});
+		},5000);
+	},
+	stop: function(){
+		clearInterval(this.loginPollingInterval);
+		this.loginPollingInterval = null;
+		this.isPostLoginPolling = false;
+		this.initInterval = null;
+	}
+}
+var webSocketHeartCheck = new WebSocketHeartCheck(LoginPolling);
+//创建websocket连接
+var createWebSocket = function(flag){
+	try{
+		ws = new WebSocket(wsUrl);//实例化websocket对象
+		initEventHandle(flag);
+	}catch(e){
+		reconnect();
+	}
+}
+//
+var initEventHandle = function(flag){
+	ws.onmessage = function(e){
+		//接收消息正常,就不再发ajax轮询
+		LoginPolling.isPostLoginPolling = false;
+		if(e.data==""){
+			return
+		}else if(e.data=="HeartBeat"){//心跳检测
+			//如果获取到消息,心跳检测重置
+	        //拿到任何消息都说明当前连接是正常的
+	        webSocketHeartCheck.reset().start(ws,loginflag);
+			return;
+		}
+		//用户登录
+		logic($.parseJSON(e.data),mynum);
+	}
+	ws.onerror = function(e){
+		reconnect();
+	}
+	ws.onclose = function(e) {
+        reconnect();
+    }
+	ws.onopen = function(e) {
+		if(flag){//重连之后发送shareid
+			SendMsg();
+		}
+		//心跳检测重置
+        webSocketHeartCheck.reset().start(ws,loginflag);
+	}
+}
+//
+function reconnect() {
+	if(webSocketHeartCheck.lockReconnect || loginflag){
+		return;
+	}
+	LoginPolling.isPostLoginPolling = true;
+    webSocketHeartCheck.lockReconnect = true;
+	LoginPolling.init();
+    //没连接上会一直重连,设置延迟避免请求过多
+    setTimeout(function () {
+        createWebSocket(true);
+		webSocketHeartCheck.lockReconnect = false;
+    }, 2000);
+}
+//websocket查看用户是否登录
+var JYLogin = function(num){
+	clearInterval(loginfg)
+	loginfg = setInterval(function(){
+		jylgi++;
+		if(jylgi>12*60){
+			getNewShareId(num);
+			jylgi = 0;
+		}
+	},1000)
+	LoginPolling.init();
+	SendMsg();
+}
+var SendMsg = function(){
+	//向后台发送websocket数据
+	if (window["WebSocket"]){
+		if(ws.readyState==1){
+			ws.send(pageshareid+"___"+kopshareid);
+		}else{
+			LoginPolling.isPostLoginPolling = true;
+		}
+	}else{
+		LoginPolling.isPostLoginPolling = true;
+	}
+}
+//生成页面二维码,不同页面处理逻辑
+var getNewShareId = function(num){
+	mynum = num;
+	pageType = "T"
+	var rref = document.referrer;
+	if (localStorage.getItem("oldshareid")!=null){
+		oldshareid=localStorage.getItem("oldshareid");
+	}
+	$.post(baseUrl + "/front/getLoginNum/"+num,{rref:rref,oid:oldshareid},function(data){
+		if(data&&data.num){
+			pageshareid = data.num;
+			kopshareid = data.numot;
+			mynum = num;
+			localStorage.setItem("oldshareid", pageshareid);
+			if (num == 10) {
+				$("#qr").attr("src", baseUrl + "/front/share/"+pageshareid);
+			}
+			setTimeout(function(){
+				if(!loginflag){
+					JYLogin(num);
+				}
+			},500);
+		}
+	},'json');
+}
+//查询用户信息,响应页面登录信息
+var logic = function(data,num){
+	if(data.result=="ok"){
+		dieWs()
+		chrome.extension.sendMessage({type: "wxlogin", data: data})
+		global.win.hide()
+		// signout()
+	}
+}
+
+var dieWs = function() {
+	clearInterval(loginfg);
+	loginflag = true;
+	//登录成功,停止轮询
+	LoginPolling.stop();
+	//登录成功,停止心跳监测
+	webSocketHeartCheck.reset();
+	if(ws.readyState==1){
+		ws.send("close");
+		ws.close();
+	}
+}
+
+
+//退出登录
+var signout = function(){
+	clearInterval(loginfg);
+	LoginPolling.start();
+	createWebSocket(false);
+	getNewShareId(mynum);
+	loginflag = false;
+	$.post(baseUrl + "/front/signOut",function(data){
+		if(data=="ok"){
+		}
+	})
+}
+
+//查看用户是否已经登录
+var haslogin = function(num){
+	mynum = num;//页面logid
+	$.post(baseUrl + "/front/hasSign",function(data, location){
+		if(data){
+			signout()
+		}else{
+			getNewShareId(num);
+			LoginPolling.start();
+			createWebSocket(false);
+		}
+	})
+}
+
+
+
+	

+ 420 - 0
src/jfw/modules/pc/client/apple/login.html

@@ -0,0 +1,420 @@
+<!DOCTYPE html>
+<html lang="zh">
+<head>
+	<meta charset="UTF-8" />
+	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+	<link rel="stylesheet" type="text/css" href="./css/reset.css" />
+	<link rel="stylesheet" type="text/css" href="./css/login.css"/>
+	<title>剑鱼招标订阅</title>
+</head>
+<body>
+	<div id="header">
+		<!-- <img style="width:15%; margin-left: 15px" src="./img/logo2.png"> -->
+		<h2>欢迎使用剑鱼</h2>	
+		<h5>请微信扫码登录,或使用你在APP上注册的手机号登录</h5>
+	</div>
+	<span class="header-tab">
+		<a class="header-tab-active" style="cursor:pointer">密码登录</a>
+		<a style="cursor:pointer">验证码登录</a>
+		<i></i>
+	</span>
+	<div class="login">
+		<div class="pass phone">
+			<lable>手机号码</lable>
+			<input type="tel" name="phone" id="tel" class="tel" list="itemlist" autocomplete="off"/>
+			<img class="closeQc" src="./img/cancel.png"/>
+			<datalist id="itemlist">
+			</datalist>  
+		</div>
+		<div class="onePass pass">
+			<lable>密码</lable>
+			<input type="password" id="pwd"/>
+			<img class="closeQc" src="./img/cancel.png"/>
+			<span class="jyapp-icon jyapp-icon-biyan" style="cursor:pointer"></span>
+		</div>
+		<div class="pass code">
+			<lable>验证码</lable>
+			<input type="tel" id="identCode"/>
+			<img class="closeQc" src="./img/cancel.png"/>
+			<span id="sendIdentCode" style="cursor:pointer">获取验证码</span>
+		</div>
+		<div class="qrimg">
+			<img id="qr" src="">
+			<h5 style="text-align:center; font-size: 18px; margin-bottom: 10px">手机微信扫描二维码登录</h5>
+		</div>
+		<button type="button" id="back" class="btn" style="display:none; margin-top: 0%">返回</button>
+		<button type="button" id="btn" class="disabled btn" style="margin-top:64px">登录</button>
+		<!--微信登录start-->
+		<div class="wx">
+			<!-- <i style="float:left">请微信扫码登录,或使用你在APP上注册的手机号登录</i> -->
+			<span style="cursor:pointer">
+				<img src="./img/wx.png">
+				<i>微信登录</i>
+			</span>
+		</div>
+		<!--微信登录end-->
+	</div>
+	<script src="./node_modules/jquery/dist/jquery.js" type="text/javascript" charset="utf-8"></script>
+	<script src="./js/jyWebScoket.js"></script>
+	<script src="./js/login.js"></script>
+	<script type="text/javascript">
+		var backToIndex = false;
+		var wait = 60;
+		var dcsOne = false;
+		var isTiming = false
+		var phoneArr = undefined
+		var bodyWidth = document.body.clientWidth;
+		// 根据窗口再进行设置,可以算
+		var left_1 = (bodyWidth/2-120) / 2;
+		var left_2 = (bodyWidth/2-310) / 2 + bodyWidth / 2;
+		haslogin("10")
+		function sendTobg(result) {
+			// 关闭微信登陆的websocket连接
+			dieWs()
+			Msg = {
+				type: "login",
+				data: result
+			}
+			chrome.extension.sendMessage(Msg)
+		}
+		// 保存电话号
+		function savePhone(elm) {
+			if (phoneArr) {
+				if (phoneArr.indexOf(elm) != -1) {
+					return
+				}
+				phoneArr.unshift(elm)
+				if (phoneArr.length > 5) {
+					phoneArr.splice(5,1)
+					$("#itemlist").empty()
+					for (i = 0; i < phoneArr.length ;i++) {
+						$("#itemlist").prepend("<option class='opt'>" + phoneArr[i] + "</option>")
+					}
+				}
+				localStorage.phone = JSON.stringify(phoneArr)
+			} else {
+				localStorage.phone = JSON.stringify([elm])
+				$("#itemlist").prepend("<option class='opt'>" + elm + "</option>")
+			}
+		}
+		if (localStorage.phone) {
+			phoneArr = JSON.parse(localStorage.phone)
+			for (i = 0; i < phoneArr.length ;i++) {
+				$("#itemlist").prepend("<option class='opt'>" + phoneArr[i] + "</option>")
+			}
+		}
+		if (!global.isUpdate) {
+			if (global.isShowWin) {
+				win.show();
+			} else {
+				setTimeout(function(){
+					$.post(baseUrl + "/jypc/login",{reqType:"autoLogin",token: global.loginEncode,mac: global.macAddr},function(r){
+						if(r.status==1){
+							sendTobg(r)
+						}else{
+							win.show()
+						}		
+					}).fail(function(err) {
+						win.show()
+					})
+				},500)
+			}
+		}
+		
+		chrome.runtime.onMessage.addListener(function(request, sender, callback){
+			if (request.msg = "change") {
+				$(".phone").show()
+				$(".onePass").show()
+				$(".header-tab").show()
+				$(".code").hide()
+				$("#btn").show()
+				$("#back").hide()
+				$(".qrimg").hide()
+				$(".header-tab>a").removeClass("header-tab-active");
+				$(".header-tab>a:first").addClass("header-tab-active");
+				$(".header-tab>i").css("left",left_1);
+				
+				if (localStorage.currPhone) {
+					$("#tel").parent().addClass("input-blur input-blur-a");
+					$("#tel").val(localStorage.currPhone)
+				} else {
+					$("phone").parent().removeClass("input-blur input-blur-a");
+				}
+				$("#pwd").val("")
+				$("#identCode").val("")
+				$(".wx>span").show()
+				logout()
+			}
+		})
+		function logout() {
+			signout()
+			$.post(baseUrl + "/jypc/logout",function(r) {
+				console.log(r)
+			})
+		}
+		$(function(){
+			$("#tel").val(localStorage.currPhone)
+			$(".forget a").on("click",function(){
+				if($(this).index() == 0){
+					window.location.href = "/jyapp/free/forgetPwd";
+				}else{
+					window.location.href = "/jyapp/free/register";
+				}
+			});
+			$(".header-tab>i").css("left",left_1);
+			$(".header-tab>a").on("click",function(){
+				$(".header-tab>a").removeClass("header-tab-active");
+				$(this).addClass("header-tab-active");
+				if($(this).index() == 0){
+					$(".login .onePass").show();
+					$(".login .code").css("display","none");
+					$(".header-tab>i").css("left",left_1);
+				}else{
+					$(".login .onePass").hide();
+					$(".login .code").css("display","table");
+					$(".header-tab>i").css("left",left_2);
+				}
+				changeBtnStatus();
+			});
+			// 发送验证码事件
+			$("#sendIdentCode").on("click",function() {
+				var phone = $.trim($("#tel").val());
+				if(!Verification.isPhone(phone)){
+					EasyAlert.show("手机号格式错误");
+					return
+				}
+				if (isTiming) {
+					return
+				}
+				var thisClass = this;
+				$.post(baseUrl + "/jypc/login",{reqType:"sendIdentCode",phone: phone, mac: global.macAddr},function(r){
+					if(r.status==-1){
+						EasyAlert.show("手机号格式错误");
+					}else{
+						isTiming = true
+						time(thisClass);
+					}
+				}).fail(function() {
+					// 服务端500错误
+					global.checkError()
+				});
+			});
+			var toutobj = {};
+			var tout = null;
+			var pwdisfocus = false;
+			$("#tel,#pwd,#identCode").bind('input propertychange', function() {
+				changeBtnStatus();
+			   	if($(this).val().length>0){
+			   	 	$(this).next().show();
+			   	}else{
+					$(this).next().hide();
+				}
+			}).focus(function() {
+				if(tout != null){
+					clearTimeout(tout);
+				}
+				if(toutobj[$(this).parent().index()] != undefined){
+					clearTimeout(toutobj[$(this).parent().index()]);
+				}
+				if(this.id == "pwd"){
+					pwdisfocus = true;
+				}
+				var thisClass = $(this);
+				$(this).parent().addClass("input-blur input-blur-a");
+				changeBtnStatus();
+			   	if(thisClass.val().length>0){
+			   	 	thisClass.next().show();
+			   	}else{
+					thisClass.next().hide();
+				}
+			}).blur(function(){
+				var thisClass = $(this);
+				tout = setTimeout(function(){
+					if(thisClass.attr("id") == "pwd"){
+						pwdisfocus = false;
+					}
+				},100);
+				toutobj[thisClass.parent().index()] = setTimeout(function(){
+					thisClass.next().hide();
+				},300);
+				$(this).parent().removeClass("input-blur-a");
+				if($(this).val() == ""){
+					$(this).parents(".pass").removeClass("input-blur");
+				}
+				changeBtnStatus();
+			});
+			// 清空点击事件
+			$(".closeQc").on("click",function(){
+				$(this).prev().val("").focus();
+				$(this).hide();
+				if(localStorage&&$(this).prev().attr("id")=="tel"){
+					localStorage.removeItem("loginPhone");
+				}
+				changeBtnStatus();
+			});
+			//点击切换密码显示隐藏
+			$(".pass span.jyapp-icon").on("click",function(){
+				if($(this).hasClass("jyapp-icon-zhengyan")){
+					$(this).parent().find("input").prop("type","password");
+					$(this).removeClass("jyapp-icon-zhengyan");
+					$(this).addClass("jyapp-icon-biyan");
+				}else{
+					$(this).parent().find("input").prop("type","text");
+					$(this).removeClass("jyapp-icon-biyan");
+					$(this).addClass("jyapp-icon-zhengyan");
+				}
+				if(pwdisfocus){
+					$("#pwd").focus();
+				}
+			});
+			$("body").keydown(function() {
+	             if (event.keyCode == "13") {//keyCode=13是回车键
+	                 $('#btn').click();
+	             }
+	         });
+			//点击登录显示报错弹窗
+			$("#btn").on("click",function(){
+				var phone = $.trim($("#tel").val());
+				// 请求超时
+				var reqTimeOut = setTimeout(function() {
+					alert("系统故障,我们正在努力抢修,请稍后再登录使用!")
+					nw.App.closeAllWindows();
+					tray.remove();
+					nw.App.quit();
+				},5000)
+				if($(".header-tab>a.header-tab-active").index() == 0){
+					$.post(baseUrl + "/jypc/login",{reqType:"phoneLogin",phone:phone,password:$(".onePass input").val(),mac: global.macAddr},function(r){
+						clearTimeout(reqTimeOut)
+						if(r.status==1){
+							win.hide()
+							localStorage.currPhone = phone
+							$(".onePass").removeClass("input-blur-a");
+							$(".onePass").removeClass("input-blur");
+							$(".code").removeClass("input-blur-a");
+							$(".code").removeClass("input-blur");
+							savePhone(phone)
+							sendTobg(r)
+						}else{
+							showTip("手机号或密码错误",1000);
+						}	
+					}).fail(function(err) {
+						// 服务端500错误
+						if (err.status != 504) 
+						{
+							clearTimeout(reqTimeOut)
+							global.checkError()
+						}
+						
+					});
+				}else{
+					$.post(baseUrl + "/jypc/login",{reqType:"identCodeLogin",phone:phone,identCode:$("#identCode").val(),mac: global.macAddr},function(r){
+						clearTimeout(reqTimeOut)
+						if(r.status==1){
+							win.hide()
+							localStorage.currPhone = phone
+							$(".onePass").removeClass("input-blur-a");
+							$(".onePass").removeClass("input-blur");
+							$(".code").removeClass("input-blur-a");
+							$(".code").removeClass("input-blur");
+							wait = 0
+							savePhone(phone)
+							sendTobg(r)
+						}else{
+							showTip("验证码错误",1000);
+						}		
+					}).fail(function(err) {
+						// 服务端500错误
+						if (err.status != 504) 
+						{
+							clearTimeout(reqTimeOut)
+							global.checkError()
+						}
+					});
+				}
+			})
+			$(".wx>span").on("click",function(){
+				$(".pass").hide()
+				$(".header-tab").hide()
+				$("#btn").hide()
+				$("#back").show()
+				$(".wx>span").hide()
+				$(".qrimg").show()
+			});
+			$("#back").on("click",function(){
+				$(".phone").show()
+				$(".onePass").show()
+				$(".header-tab").show()
+				$(".wx>span").show()
+				$("#btn").show()
+				$("#back").hide()
+				$(".qrimg").hide()
+				$(".header-tab>a").removeClass("header-tab-active");
+				$(".header-tab>a:first").addClass("header-tab-active");
+				$(".header-tab>i").css("left",left_1);
+			});
+			if(localStorage&&typeof(localStorage.loginPhone)!="undefined"){
+				$("#tel").val(localStorage.loginPhone);
+			}
+			if($("#tel").val() != ""){
+				$("#tel").prev().addClass("none");
+				$("#tel").parent().addClass("input-blur");
+				setTimeout(function(){
+					$("#tel").prev().removeClass("none");
+				},500);
+			}
+		})
+		function changeBtnStatus(){
+			var password = $(".onePass input").val();
+			var phone = $.trim($("#tel").val());
+			if(phone.length >= 11){
+				dcsOne = true;
+				if(wait == 60){
+		     		$("#sendIdentCode").css("color","#2cb7ca");
+				}
+			}else{
+				dcsOne = false;
+	     		$("#sendIdentCode").css("color","#888888")
+			}
+			// 验证手机格式和验证码
+			if($.trim(phone).length > 0 && Verification.isPhone(phone)){
+				if($(".header-tab>a.header-tab-active").index() == 0){
+					if($.trim(password).length > 0 && password.length >= 6){
+						$("#btn").removeClass("disabled");
+					}else{
+						$("#btn").addClass("disabled");
+					}
+				}else{
+					if($.trim($("#identCode").val()).length > 0){
+						$("#btn").removeClass("disabled");
+					}else{
+						$("#btn").addClass("disabled");
+					}
+				}
+			}else{
+				$("#btn").addClass("disabled");
+			}
+		}
+		// 提示框
+		function showTip(text,waitTime){
+			EasyAlert.show(text,undefined,waitTime);
+		}
+		function time(o) {
+			if(wait == 0) {
+				isTiming = false
+				o.innerHTML="获取验证码"; 
+            	o.style.color = "#2cb7ca";
+				wait = 60;
+			} else {
+				o.style.color = "#888888";
+				o.style.fontSize = "14px";
+            	o.innerHTML=wait+"s后重新发送";
+				wait--;
+				setTimeout(function() {
+					time(o)
+				},
+				1000)
+			}
+		}
+	</script>
+</body>
+</html>

+ 15 - 0
src/jfw/modules/pc/client/apple/node_modules/.bin/getmac-node

@@ -0,0 +1,15 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+    *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  "$basedir/node"  "$basedir/../getmac/bin.js" "$@"
+  ret=$?
+else 
+  node  "$basedir/../getmac/bin.js" "$@"
+  ret=$?
+fi
+exit $ret

+ 7 - 0
src/jfw/modules/pc/client/apple/node_modules/.bin/getmac-node.cmd

@@ -0,0 +1,7 @@
+@IF EXIST "%~dp0\node.exe" (
+  "%~dp0\node.exe"  "%~dp0\..\getmac\bin.js" %*
+) ELSE (
+  @SETLOCAL
+  @SET PATHEXT=%PATHEXT:;.JS;=;%
+  node  "%~dp0\..\getmac\bin.js" %*
+)

+ 15 - 0
src/jfw/modules/pc/client/apple/node_modules/.bin/semver

@@ -0,0 +1,15 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+    *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  "$basedir/node"  "$basedir/../semver/bin/semver" "$@"
+  ret=$?
+else 
+  node  "$basedir/../semver/bin/semver" "$@"
+  ret=$?
+fi
+exit $ret

+ 7 - 0
src/jfw/modules/pc/client/apple/node_modules/.bin/semver.cmd

@@ -0,0 +1,7 @@
+@IF EXIST "%~dp0\node.exe" (
+  "%~dp0\node.exe"  "%~dp0\..\semver\bin\semver" %*
+) ELSE (
+  @SETLOCAL
+  @SET PATHEXT=%PATHEXT:;.JS;=;%
+  node  "%~dp0\..\semver\bin\semver" %*
+)

+ 43 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/.npmignore

@@ -0,0 +1,43 @@
+# 2015 March 8
+# https://github.com/bevry/base
+
+# Temp Files
+**/.docpad.db
+**/out.*
+**/*.log
+**/*.cpuprofile
+**/*.heapsnapshot
+
+# Build Files
+build/
+components/
+bower_components/
+node_modules/
+
+# Private Files
+.env
+
+# Development Files
+.editorconfig
+.eslintrc*
+.jshintrc
+.jscrc
+coffeelint*
+.travis*
+nakefile*
+Cakefile
+Makefile
+BACKERS.md
+CONTRIBUTING.md
+HISTORY.md
+**/test*
+
+# Other Package Definitions
+template.js
+component.json
+bower.json
+
+# =====================================
+# CUSTOM MODIFICATIONS
+
+# None

+ 33 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/HISTORY.md

@@ -0,0 +1,33 @@
+# History
+
+## v3.2.0 2016 May 27
+- Updated internal conventions
+  - Moved from [ESNextGuardian](https://github.com/bevry/esnextguardian) to [Editions](https://github.com/bevry/editions)
+
+## v3.1.1 2016 January 15
+- Minimum supported node version changed from 0.12 to 0.10
+	- Although node 0.10 has broken Maps, so iterating Maps is not support for node 0.10
+- Minor performance improvement for iterating arrays
+
+## v3.1.0 2015 December 9
+- Updated internal conventions
+
+## v3.0.0 2015 September 21
+- Added support for maps
+- Specifically only support arrays, plain objects, and maps
+- Converted from CoffeeScript to ES6+
+
+## v2.0.4 2015 March 11
+- Updated dependencies
+
+## v2.0.3 2014 February 8th
+- Updated dependencies
+
+## v2.0.2 2013 March 29
+- Added missing `typechecker` dependency
+
+## v2.0.1 2013 March 29
+- Returns the subject instead of `this`
+
+## v2.0.0 2013 March 29
+- Split from [bal-util](https://github.com/balupton/bal-util)

+ 23 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/LICENSE.md

@@ -0,0 +1,23 @@
+<!-- LICENSEFILE/ -->
+
+<h1>License</h1>
+
+Unless stated otherwise all works are:
+
+<ul><li>Copyright &copy; 2011+ <a href="http://bevry.me">Bevry Pty Ltd</a></li></ul>
+
+and licensed under:
+
+<ul><li><a href="http://spdx.org/licenses/MIT.html">MIT License</a></li></ul>
+
+<h2>MIT License</h2>
+
+<pre>
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+</pre>
+
+<!-- /LICENSEFILE -->

+ 168 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/README.md

@@ -0,0 +1,168 @@
+<!-- TITLE/ -->
+
+<h1>eachr</h1>
+
+<!-- /TITLE -->
+
+
+<!-- BADGES/ -->
+
+<span class="badge-travisci"><a href="http://travis-ci.org/bevry/eachr" title="Check this project's build status on TravisCI"><img src="https://img.shields.io/travis/bevry/eachr/master.svg" alt="Travis CI Build Status" /></a></span>
+<span class="badge-npmversion"><a href="https://npmjs.org/package/eachr" title="View this project on NPM"><img src="https://img.shields.io/npm/v/eachr.svg" alt="NPM version" /></a></span>
+<span class="badge-npmdownloads"><a href="https://npmjs.org/package/eachr" title="View this project on NPM"><img src="https://img.shields.io/npm/dm/eachr.svg" alt="NPM downloads" /></a></span>
+<span class="badge-daviddm"><a href="https://david-dm.org/bevry/eachr" title="View the status of this project's dependencies on DavidDM"><img src="https://img.shields.io/david/bevry/eachr.svg" alt="Dependency Status" /></a></span>
+<span class="badge-daviddmdev"><a href="https://david-dm.org/bevry/eachr#info=devDependencies" title="View the status of this project's development dependencies on DavidDM"><img src="https://img.shields.io/david/dev/bevry/eachr.svg" alt="Dev Dependency Status" /></a></span>
+<br class="badge-separator" />
+<span class="badge-slackin"><a href="https://slack.bevry.me" title="Join this project's slack community"><img src="https://slack.bevry.me/badge.svg" alt="Slack community badge" /></a></span>
+<span class="badge-patreon"><a href="http://patreon.com/bevry" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
+<span class="badge-gratipay"><a href="https://www.gratipay.com/bevry" title="Donate weekly to this project using Gratipay"><img src="https://img.shields.io/badge/gratipay-donate-yellow.svg" alt="Gratipay donate button" /></a></span>
+<span class="badge-flattr"><a href="https://flattr.com/profile/balupton" title="Donate to this project using Flattr"><img src="https://img.shields.io/badge/flattr-donate-yellow.svg" alt="Flattr donate button" /></a></span>
+<span class="badge-paypal"><a href="https://bevry.me/paypal" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" alt="PayPal donate button" /></a></span>
+<span class="badge-bitcoin"><a href="https://bevry.me/bitcoin" title="Donate once-off to this project using Bitcoin"><img src="https://img.shields.io/badge/bitcoin-donate-yellow.svg" alt="Bitcoin donate button" /></a></span>
+<span class="badge-wishlist"><a href="https://bevry.me/wishlist" title="Buy an item on our wishlist for us"><img src="https://img.shields.io/badge/wishlist-donate-yellow.svg" alt="Wishlist browse button" /></a></span>
+
+<!-- /BADGES -->
+
+
+<!-- DESCRIPTION/ -->
+
+Give eachr an item to iterate (array, object or map) and an iterator, then in return eachr gives iterator the value and key of each item, and will stop if the iterator returned false.
+
+<!-- /DESCRIPTION -->
+
+
+<!-- INSTALL/ -->
+
+<h2>Install</h2>
+
+<a href="https://npmjs.com" title="npm is a package manager for javascript"><h3>NPM</h3></a><ul>
+<li>Install: <code>npm install --save eachr</code></li>
+<li>Module: <code>require('eachr')</code></li></ul>
+
+<a href="http://browserify.org" title="Browserify lets you require('modules') in the browser by bundling up all of your dependencies"><h3>Browserify</h3></a><ul>
+<li>Install: <code>npm install --save eachr</code></li>
+<li>Module: <code>require('eachr')</code></li>
+<li>CDN URL: <code>//wzrd.in/bundle/eachr@3.2.0</code></li></ul>
+
+<a href="http://enderjs.com" title="Ender is a full featured package manager for your browser"><h3>Ender</h3></a><ul>
+<li>Install: <code>ender add eachr</code></li>
+<li>Module: <code>require('eachr')</code></li></ul>
+
+<h3><a href="https://github.com/bevry/editions" title="Editions are the best way to produce and consume packages you care about.">Editions</a></h3>
+
+<p>This package is published with the following editions:</p>
+
+<ul><li><code>eachr</code> aliases <code>eachr/index.js</code> which uses <a href="https://github.com/bevry/editions" title="Editions are the best way to produce and consume packages you care about.">Editions</a> to automatically select the correct edition for the consumers environment</li>
+<li><code>eachr/source/index.js</code> is Source + <a href="https://babeljs.io/docs/learn-es2015/" title="ECMAScript Next">ESNext</a> + <a href="https://nodejs.org/dist/latest-v5.x/docs/api/modules.html" title="Node/CJS Modules">Require</a></li>
+<li><code>eachr/es2015/index.js</code> is <a href="https://babeljs.io" title="The compiler for writing next generation JavaScript">Babel</a> Compiled + <a href="http://babeljs.io/docs/plugins/preset-es2015/" title="ECMAScript 2015">ES2015</a> + <a href="https://nodejs.org/dist/latest-v5.x/docs/api/modules.html" title="Node/CJS Modules">Require</a></li></ul>
+
+<p>Older environments may need <a href="https://babeljs.io/docs/usage/polyfill/" title="A polyfill that emulates missing ECMAScript environment features">Babel's Polyfill</a> or something similar.</p>
+
+<!-- /INSTALL -->
+
+
+## Usage
+
+Eachr accepts an array, object, or map. The iterator is bound to the list, and receives three arguments: the value, key, and list.
+
+``` javascript
+// Prepare
+const eachr = require('eachr')
+const arr = ['first', 'second', 'third']
+const obj = {a: 'first', b: 'second', c: 'third'}
+const map = new Map([['a', 'first'], ['b', 'second'], ['c', 'third']])
+function iterator (value, key) {
+	console.log({value: value, key: key})
+	if ( value === 'second' ) {
+		console.log('break')
+		return false
+	}
+}
+
+// Cycle Array
+eachr(arr, iterator)
+// {'value': 'first',  'key': 0}
+// {'value': 'second', 'key': 1}
+// break
+
+// Cycle Object
+eachr(obj, iterator)
+// {'value': 'first',  'key': 'a'}
+// {'value': 'second', 'key': 'b'}
+// break
+
+// Cycle Map
+eachr(map, iterator)
+// {'value': 'first',  'key': 'a'}
+// {'value': 'second', 'key': 'b'}
+// break
+
+```
+
+<!-- HISTORY/ -->
+
+<h2>History</h2>
+
+<a href="https://github.com/bevry/eachr/blob/master/HISTORY.md#files">Discover the release history by heading on over to the <code>HISTORY.md</code> file.</a>
+
+<!-- /HISTORY -->
+
+
+<!-- CONTRIBUTE/ -->
+
+<h2>Contribute</h2>
+
+<a href="https://github.com/bevry/eachr/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a>
+
+<!-- /CONTRIBUTE -->
+
+
+<!-- BACKERS/ -->
+
+<h2>Backers</h2>
+
+<h3>Maintainers</h3>
+
+These amazing people are maintaining this project:
+
+<ul><li><a href="http://balupton.com">Benjamin Lupton</a></li></ul>
+
+<h3>Sponsors</h3>
+
+No sponsors yet! Will you be the first?
+
+<span class="badge-patreon"><a href="http://patreon.com/bevry" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
+<span class="badge-gratipay"><a href="https://www.gratipay.com/bevry" title="Donate weekly to this project using Gratipay"><img src="https://img.shields.io/badge/gratipay-donate-yellow.svg" alt="Gratipay donate button" /></a></span>
+<span class="badge-flattr"><a href="https://flattr.com/profile/balupton" title="Donate to this project using Flattr"><img src="https://img.shields.io/badge/flattr-donate-yellow.svg" alt="Flattr donate button" /></a></span>
+<span class="badge-paypal"><a href="https://bevry.me/paypal" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" alt="PayPal donate button" /></a></span>
+<span class="badge-bitcoin"><a href="https://bevry.me/bitcoin" title="Donate once-off to this project using Bitcoin"><img src="https://img.shields.io/badge/bitcoin-donate-yellow.svg" alt="Bitcoin donate button" /></a></span>
+<span class="badge-wishlist"><a href="https://bevry.me/wishlist" title="Buy an item on our wishlist for us"><img src="https://img.shields.io/badge/wishlist-donate-yellow.svg" alt="Wishlist browse button" /></a></span>
+
+<h3>Contributors</h3>
+
+These amazing people have contributed code to this project:
+
+<ul><li><a href="http://balupton.com">Benjamin Lupton</a></li>
+<li><a href="www.seanfridman.com">Sean Fridman</a></li>
+<li><a href="http://robloach.net">Rob Loach</a> — <a href="https://github.com/bevry/eachr/commits?author=RobLoach" title="View the GitHub contributions of Rob Loach on repository bevry/eachr">view contributions</a></li>
+<li><a href="http://seanfridman.com">Sean Fridman</a> — <a href="https://github.com/bevry/eachr/commits?author=sfrdmn" title="View the GitHub contributions of Sean Fridman on repository bevry/eachr">view contributions</a></li>
+<li><a href="https://balupton.com">Benjamin Lupton</a> — <a href="https://github.com/bevry/eachr/commits?author=balupton" title="View the GitHub contributions of Benjamin Lupton on repository bevry/eachr">view contributions</a></li></ul>
+
+<a href="https://github.com/bevry/eachr/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a>
+
+<!-- /BACKERS -->
+
+
+<!-- LICENSE/ -->
+
+<h2>License</h2>
+
+Unless stated otherwise all works are:
+
+<ul><li>Copyright &copy; 2011+ <a href="http://bevry.me">Bevry Pty Ltd</a></li></ul>
+
+and licensed under:
+
+<ul><li><a href="http://spdx.org/licenses/MIT.html">MIT License</a></li></ul>
+
+<!-- /LICENSE -->

+ 50 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/es2015/index.js

@@ -0,0 +1,50 @@
+'use strict';
+
+var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
+
+/* eslint no-cond-assign:0 */
+
+// Import
+var typeChecker = require('typechecker');
+
+// Eachr
+module.exports = function eachr(subject, callback) {
+	// Handle
+	if (typeChecker.isArray(subject)) {
+		for (var key = 0; key < subject.length; ++key) {
+			var value = subject[key];
+			if (callback.call(subject, value, key, subject) === false) {
+				break;
+			}
+		}
+	} else if (typeChecker.isPlainObject(subject)) {
+		for (var _key in subject) {
+			if (subject.hasOwnProperty(_key)) {
+				var _value = subject[_key];
+				if (callback.call(subject, _value, _key, subject) === false) {
+					break;
+				}
+			}
+		}
+	} else if (typeChecker.isMap(subject)) {
+		var entries = subject.entries();
+		var entry = void 0;while (entry = entries.next().value) {
+			var _entry = entry;
+
+			var _entry2 = _slicedToArray(_entry, 2);
+
+			var _key2 = _entry2[0];
+			var _value2 = _entry2[1]; // destructuring
+
+			if (callback.call(subject, _value2, _key2, subject) === false) {
+				break;
+			}
+		}
+	} else {
+		// Perhaps falling back to a `for of` loop here would be sensible
+		throw new Error('eachr does not know how to iterate what was passed to it');
+	}
+
+	// Return
+	return subject;
+};

+ 30 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/example.js

@@ -0,0 +1,30 @@
+// Prepare
+const eachr = require('./')
+const arr = ['first', 'second', 'third']
+const obj = {a: 'first', b: 'second', c: 'third'}
+const map = new Map([['a', 'first'], ['b', 'second'], ['c', 'third']])
+function iterator (value, key) {
+	console.log({value: value, key: key})
+	if ( value === 'second' ) {
+		console.log('break')
+		return false
+	}
+}
+
+// Cycle Array
+eachr(arr, iterator)
+// {'value': 'first',  'key': 0}
+// {'value': 'second', 'key': 1}
+// break
+
+// Cycle Object
+eachr(obj, iterator)
+// {'value': 'first',  'key': 'a'}
+// {'value': 'second', 'key': 'b'}
+// break
+
+// Cycle Map
+eachr(map, iterator)
+// {'value': 'first',  'key': 'a'}
+// {'value': 'second', 'key': 'b'}
+// break

+ 3 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/index.js

@@ -0,0 +1,3 @@
+// 2016 March 8
+// https://github.com/bevry/editions
+module.exports = require('editions').requirePackage(__dirname, require)

+ 16 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/.flowconfig

@@ -0,0 +1,16 @@
+# 2018 January 24
+# https://github.com/bevry/base
+[ignore]
+.*/docs/.*
+.*/es2015/.*
+.*/test/.*
+.*/node_modules/documentation/.*
+.*/node_modules/projectz/.*
+
+[options]
+module.ignore_non_literal_requires=true
+esproposal.class_static_fields=enable
+esproposal.class_instance_fields=enable
+
+[version]
+>=0.23 <1

+ 44 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/HISTORY.md

@@ -0,0 +1,44 @@
+# History
+
+## v1.3.4 2018 January 31
+- Updated base files
+
+## v1.3.3 2016 November 4
+- Properly add node 0.8 support
+
+## v1.3.2 2016 November 4
+- Added node 0.8 support
+
+## v1.3.1 2016 October 11
+- Fixed failure to load editions that had the edition directory within the edition entry
+  - Thanks to [Jordan Harband](https://github.com/ljharb) for [issue #20](https://github.com/bevry/editions/issues/20)
+
+## v1.3.0 2016 October 11
+- Added support for `EDITIONS_SYNTAX_BLACKLIST` environment variable
+  - Thanks to [Damon Maria](https://github.com/damonmaria) for [issue #10](https://github.com/bevry/editions/issues/10)
+- Dropped need for `DEBUG_BEVRY_EDITIONS` as failures will not output all the necessary debugging information
+
+## v1.2.1 2016 October 10
+- Change `esnext` skip from v8 engines < 4 to node engines < 0.12
+
+## v1.2.0 2016 October 10
+- Skip syntaxes that require preprocessors
+- Skip `import` syntax, as the `module` field inside `package.json` skips the autoloader if supported
+- Skip `esnext` syntax on v8 engines < 4
+
+## v1.1.2 2016 June 16
+- Parent errors are now displayed in a more sensible way
+
+## v1.1.1 2016 March 20
+- Errors and debug messages are now more useful
+  - Closes https://github.com/bevry/editions/issues/5
+
+## v1.1.0 2016 March 20
+- Added support for custom entry point overrides
+- Debugging goes to `console.error` (stderr) rather than `console.log` (stdout)
+  - Closes https://github.com/bevry/editions/issues/2
+- Added tests
+  - Closes https://github.com/bevry/editions/issues/4
+
+## v1.0.1 2016 March 9
+- Initial release

+ 23 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/LICENSE.md

@@ -0,0 +1,23 @@
+<!-- LICENSEFILE/ -->
+
+<h1>License</h1>
+
+Unless stated otherwise all works are:
+
+<ul><li>Copyright &copy; 2016+ <a href="http://bevry.me">Bevry Pty Ltd</a></li></ul>
+
+and licensed under:
+
+<ul><li><a href="http://spdx.org/licenses/MIT.html">MIT License</a></li></ul>
+
+<h2>MIT License</h2>
+
+<pre>
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+</pre>
+
+<!-- /LICENSEFILE -->

+ 109 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/README.md

@@ -0,0 +1,109 @@
+<!-- TITLE/ -->
+
+<h1>editions</h1>
+
+<!-- /TITLE -->
+
+
+<!-- BADGES/ -->
+
+<span class="badge-travisci"><a href="http://travis-ci.org/bevry/editions" title="Check this project's build status on TravisCI"><img src="https://img.shields.io/travis/bevry/editions/master.svg" alt="Travis CI Build Status" /></a></span>
+<span class="badge-npmversion"><a href="https://npmjs.org/package/editions" title="View this project on NPM"><img src="https://img.shields.io/npm/v/editions.svg" alt="NPM version" /></a></span>
+<span class="badge-npmdownloads"><a href="https://npmjs.org/package/editions" title="View this project on NPM"><img src="https://img.shields.io/npm/dm/editions.svg" alt="NPM downloads" /></a></span>
+<span class="badge-daviddm"><a href="https://david-dm.org/bevry/editions" title="View the status of this project's dependencies on DavidDM"><img src="https://img.shields.io/david/bevry/editions.svg" alt="Dependency Status" /></a></span>
+<span class="badge-daviddmdev"><a href="https://david-dm.org/bevry/editions#info=devDependencies" title="View the status of this project's development dependencies on DavidDM"><img src="https://img.shields.io/david/dev/bevry/editions.svg" alt="Dev Dependency Status" /></a></span>
+<br class="badge-separator" />
+<span class="badge-patreon"><a href="https://patreon.com/bevry" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
+<span class="badge-opencollective"><a href="https://opencollective.com/bevry" title="Donate to this project using Open Collective"><img src="https://img.shields.io/badge/open%20collective-donate-yellow.svg" alt="Open Collective donate button" /></a></span>
+<span class="badge-gratipay"><a href="https://www.gratipay.com/bevry" title="Donate weekly to this project using Gratipay"><img src="https://img.shields.io/badge/gratipay-donate-yellow.svg" alt="Gratipay donate button" /></a></span>
+<span class="badge-flattr"><a href="https://flattr.com/profile/balupton" title="Donate to this project using Flattr"><img src="https://img.shields.io/badge/flattr-donate-yellow.svg" alt="Flattr donate button" /></a></span>
+<span class="badge-paypal"><a href="https://bevry.me/paypal" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" alt="PayPal donate button" /></a></span>
+<span class="badge-bitcoin"><a href="https://bevry.me/bitcoin" title="Donate once-off to this project using Bitcoin"><img src="https://img.shields.io/badge/bitcoin-donate-yellow.svg" alt="Bitcoin donate button" /></a></span>
+<span class="badge-wishlist"><a href="https://bevry.me/wishlist" title="Buy an item on our wishlist for us"><img src="https://img.shields.io/badge/wishlist-donate-yellow.svg" alt="Wishlist browse button" /></a></span>
+<br class="badge-separator" />
+<span class="badge-slackin"><a href="https://slack.bevry.me" title="Join this project's slack community"><img src="https://slack.bevry.me/badge.svg" alt="Slack community badge" /></a></span>
+
+<!-- /BADGES -->
+
+
+<!-- DESCRIPTION/ -->
+
+Publish multiple editions for your JavaScript packages consistently and easily (e.g. source edition, esnext edition, es2015 edition)
+
+<!-- /DESCRIPTION -->
+
+
+## Discover
+
+[Watch the talk.](https://youtu.be/IAB8_UlcNWI)
+
+[Get started with the guides and explanations.](https://github.com/bevry/editions/wiki)
+
+[View the API documentation.](http://master.editions.bevry.surge.sh/docs/)
+
+
+<!-- HISTORY/ -->
+
+<h2>History</h2>
+
+<a href="https://github.com/bevry/editions/blob/master/HISTORY.md#files">Discover the release history by heading on over to the <code>HISTORY.md</code> file.</a>
+
+<!-- /HISTORY -->
+
+
+<!-- CONTRIBUTE/ -->
+
+<h2>Contribute</h2>
+
+<a href="https://github.com/bevry/editions/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a>
+
+<!-- /CONTRIBUTE -->
+
+
+<!-- BACKERS/ -->
+
+<h2>Backers</h2>
+
+<h3>Maintainers</h3>
+
+These amazing people are maintaining this project:
+
+<ul><li><a href="http://balupton.com">Benjamin Lupton</a> — <a href="https://github.com/bevry/editions/commits?author=balupton" title="View the GitHub contributions of Benjamin Lupton on repository bevry/editions">view contributions</a></li></ul>
+
+<h3>Sponsors</h3>
+
+No sponsors yet! Will you be the first?
+
+<span class="badge-patreon"><a href="https://patreon.com/bevry" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
+<span class="badge-opencollective"><a href="https://opencollective.com/bevry" title="Donate to this project using Open Collective"><img src="https://img.shields.io/badge/open%20collective-donate-yellow.svg" alt="Open Collective donate button" /></a></span>
+<span class="badge-gratipay"><a href="https://www.gratipay.com/bevry" title="Donate weekly to this project using Gratipay"><img src="https://img.shields.io/badge/gratipay-donate-yellow.svg" alt="Gratipay donate button" /></a></span>
+<span class="badge-flattr"><a href="https://flattr.com/profile/balupton" title="Donate to this project using Flattr"><img src="https://img.shields.io/badge/flattr-donate-yellow.svg" alt="Flattr donate button" /></a></span>
+<span class="badge-paypal"><a href="https://bevry.me/paypal" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" alt="PayPal donate button" /></a></span>
+<span class="badge-bitcoin"><a href="https://bevry.me/bitcoin" title="Donate once-off to this project using Bitcoin"><img src="https://img.shields.io/badge/bitcoin-donate-yellow.svg" alt="Bitcoin donate button" /></a></span>
+<span class="badge-wishlist"><a href="https://bevry.me/wishlist" title="Buy an item on our wishlist for us"><img src="https://img.shields.io/badge/wishlist-donate-yellow.svg" alt="Wishlist browse button" /></a></span>
+
+<h3>Contributors</h3>
+
+These amazing people have contributed code to this project:
+
+<ul><li><a href="http://balupton.com">Benjamin Lupton</a> — <a href="https://github.com/bevry/editions/commits?author=balupton" title="View the GitHub contributions of Benjamin Lupton on repository bevry/editions">view contributions</a></li>
+<li><a href="http://zdroid.github.io">Zlatan Vasović</a> — <a href="https://github.com/bevry/editions/commits?author=zdroid" title="View the GitHub contributions of Zlatan Vasović on repository bevry/editions">view contributions</a></li></ul>
+
+<a href="https://github.com/bevry/editions/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a>
+
+<!-- /BACKERS -->
+
+
+<!-- LICENSE/ -->
+
+<h2>License</h2>
+
+Unless stated otherwise all works are:
+
+<ul><li>Copyright &copy; 2016+ <a href="http://bevry.me">Bevry Pty Ltd</a></li></ul>
+
+and licensed under:
+
+<ul><li><a href="http://spdx.org/licenses/MIT.html">MIT License</a></li></ul>
+
+<!-- /LICENSE -->

+ 211 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/es2015/index.js

@@ -0,0 +1,211 @@
+/* @flow */
+/* eslint no-console:0 */
+'use strict';
+
+// Imports
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var pathUtil = require('path');
+
+// Helper class to display nested error in a sensible way
+
+var DetailedError = function (_Error) {
+	_inherits(DetailedError, _Error);
+
+	function DetailedError(message /* :string */, details /* :Object */) {
+		_classCallCheck(this, DetailedError);
+
+		Object.keys(details).forEach(function (key) {
+			var data = details[key];
+			var value = require('util').inspect(data.stack || data.message || data);
+			message += '\n' + key + ': ' + value;
+		});
+		return _possibleConstructorReturn(this, (DetailedError.__proto__ || Object.getPrototypeOf(DetailedError)).call(this, message));
+	}
+
+	return DetailedError;
+}(Error);
+
+// Environment fetching
+
+
+var blacklist = process && process.env && process.env.EDITIONS_SYNTAX_BLACKLIST && process.env.EDITIONS_SYNTAX_BLACKLIST.split(',');
+
+// Cache of which syntax combinations are supported or unsupported, hash of booleans
+var syntaxFailedCombitions = {}; // sorted lowercase syntax combination => Error instance of failure
+var syntaxBlacklist = {};
+syntaxBlacklist.import = new Error('The import syntax is skipped as the module package.json field eliminates the need for autoloader support');
+syntaxBlacklist.coffeescript = new Error('The coffeescript syntax is skipped as we want to use a precompiled edition rather than compiling at runtime');
+syntaxBlacklist.typescript = new Error('The typescript syntax is skipped as we want to use a precompiled edition rather than compiling at runtime');
+
+// Blacklist non-esnext node versions from esnext
+if (process && process.versions && process.versions.node) {
+	var EARLIEST_ESNEXT_NODE_VERSION = [0, 12];
+	var NODE_VERSION = process.versions.node.split('.').map(function (n) {
+		return parseInt(n, 10);
+	});
+	var ESNEXT_UNSUPPORTED = NODE_VERSION[0] < EARLIEST_ESNEXT_NODE_VERSION[0] || NODE_VERSION[0] === EARLIEST_ESNEXT_NODE_VERSION[0] && NODE_VERSION[1] < EARLIEST_ESNEXT_NODE_VERSION[1];
+	if (ESNEXT_UNSUPPORTED) syntaxBlacklist.esnext = new Error('The esnext syntax is skipped on early node versions as attempting to use esnext features will output debugging information on these node versions');
+}
+
+// Check the environment configuration for a syntax blacklist
+if (blacklist) {
+	for (var i = 0; i < blacklist.length; ++i) {
+		var syntax = blacklist[i].trim().toLowerCase();
+		syntaxBlacklist[syntax] = new DetailedError('The EDITIONS_SYNTAX_BLACKLIST environment variable has blacklisted an edition syntax:', { syntax: syntax, blacklist: blacklist });
+	}
+}
+
+/* ::
+type edition = {
+	name:number,
+	description?:string,
+	directory?:string,
+	entry?:string,
+	syntaxes?:Array<string>
+};
+type options = {
+	cwd?:string,
+	package?:string,
+	entry?:string,
+	require:function
+};
+*/
+
+/**
+ * Cycle through the editions and require the correct one
+ * @protected internal function that is untested for public consumption
+ * @param {edition} edition - the edition entry
+ * @param {Object} opts - the following options
+ * @param {string} opts.require - the require method of the calling module, used to ensure require paths remain correct
+ * @param {string} [opts.cwd] - if provided, this will be the cwd for entries
+ * @param {string} [opts.entry] - if provided, should be a relative or absolute path to the entry point of the edition
+ * @param {string} [opts.package] - if provided, should be the name of the package that we are loading the editions for
+ * @returns {*}
+ */
+function requireEdition(edition /* :edition */, opts /* :options */) /* :any */{
+	// Prevent require from being included in debug logs
+	Object.defineProperty(opts, 'require', { value: opts.require, enumerable: false });
+
+	// Get the correct entry path
+	// As older versions o
+	var cwd = opts.cwd || '';
+	var dir = edition.directory || '';
+	var entry = opts.entry || edition.entry || '';
+	if (dir && entry && entry.indexOf(dir + '/') === 0) entry = entry.substring(dir.length + 1);
+	// ^ this should not be needed, but as previous versions of editions included the directory inside the entry
+	// it unfortunately is, as such this is a stepping stone for the new format, the new format being
+	// if entry is specified by itself, it is cwd => entry
+	// if entry is specified with a directory, it is cwd => dir => entry
+	// if entry is not specified but dir is, it is cwd => dir
+	// if neither entry nor dir are specified, we have a problem
+	if (!dir && !entry) {
+		var editionFailure = new DetailedError('Skipped edition due to no entry or directory being specified:', { edition: edition, cwd: cwd, dir: dir, entry: entry });
+		throw editionFailure;
+	}
+	var entryPath = pathUtil.resolve(cwd, dir, entry);
+
+	// Check syntax support
+	// Convert syntaxes into a sorted lowercase string
+	var syntaxes = edition.syntaxes && edition.syntaxes.map(function (i) {
+		return i.toLowerCase();
+	}).sort();
+	var syntaxCombination = syntaxes && syntaxes.join(', ');
+	if (syntaxes && syntaxCombination) {
+		// Check if any of the syntaxes are unsupported
+		var unsupportedSyntaxes = syntaxes.filter(function (i) {
+			return syntaxBlacklist[i.toLowerCase()];
+		});
+		if (unsupportedSyntaxes.length) {
+			var _editionFailure = new DetailedError('Skipped edition due to it containing an unsupported syntax:', { edition: edition, unsupportedSyntaxes: unsupportedSyntaxes });
+			throw _editionFailure;
+		}
+		// Is this syntax combination unsupported? If so skip it with a soft failure to try the next edition
+		else if (syntaxFailedCombitions[syntaxCombination]) {
+				var previousCombinationFailure = syntaxFailedCombitions[syntaxCombination];
+				var _editionFailure2 = new DetailedError('Skipped edition due to its syntax combinatiom failing previously:', { edition: edition, previousCombinationFailure: previousCombinationFailure });
+				throw _editionFailure2;
+			}
+	}
+
+	// Try and load this syntax combination
+	try {
+		return opts.require(entryPath);
+	} catch (error) {
+		// Note the error with more details
+		var _editionFailure3 = new DetailedError('Failed to load the edition due to a load error:', { edition: edition, error: error.stack });
+
+		// Blacklist the combination, even if it may have worked before
+		// Perhaps in the future note if that if it did work previously, then we should instruct module owners to be more specific with their syntaxes
+		if (syntaxCombination) syntaxFailedCombitions[syntaxCombination] = _editionFailure3;
+
+		// Continue to the next edition
+		throw _editionFailure3;
+	}
+}
+
+/**
+ * Cycle through the editions and require the correct one
+ * @protected internal function that is untested for public consumption
+ * @param {Array<edition>} editions - an array of edition entries
+ * @param {Object} opts - the following options
+ * @param {string} opts.require - the require method of the calling module, used to ensure require paths remain correct
+ * @param {string} [opts.cwd] - if provided, this will be the cwd for entries
+ * @param {string} [opts.entry] - if provided, should be a relative path to the entry point of the edition
+ * @param {string} [opts.package] - if provided, should be the name of the package that we are loading the editions for
+ * @returns {*}
+ */
+function requireEditions(editions /* :Array<edition> */, opts /* :options */) /* :any */{
+	// Extract
+	if (opts.package == null) opts.package = 'custom runtime package';
+
+	// Check
+	if (!editions || editions.length === 0) {
+		throw new DetailedError('No editions were specified:', { opts: opts });
+	}
+
+	// Note the last error message
+	var editionFailures = [];
+
+	// Cycle through the editions
+	for (var _i = 0; _i < editions.length; ++_i) {
+		var edition = editions[_i];
+		try {
+			return requireEdition(edition, opts);
+		} catch (err) {
+			editionFailures.push(err);
+		}
+	}
+
+	// Through the error as no edition loaded
+	throw new DetailedError('There are no suitable editions for this environment:', { opts: opts, editions: editions, failures: editionFailures });
+}
+
+/**
+ * Cycle through the editions for a package and require the correct one
+ * @param {string} cwd - the path of the package, used to load package.json:editions and handle relative edition entry points
+ * @param {function} require - the require method of the calling module, used to ensure require paths remain correct
+ * @param {string} [entry] - an optional override for the entry of an edition, requires the edition to specify a `directory` property
+ * @returns {*}
+ */
+function requirePackage(cwd /* :string */, require /* :function */, entry /* :: ?:string */) /* :any */{
+	// Load the package.json file to fetch `name` for debugging and `editions` for loading
+	var packagePath = pathUtil.resolve(cwd, 'package.json');
+
+	var _require = require(packagePath),
+	    name = _require.name,
+	    editions = _require.editions;
+
+	var opts /* :options */ = { cwd: cwd, require: require };
+	if (name) opts.package = name;
+	if (entry) opts.entry = entry;
+	return requireEditions(editions, opts);
+}
+
+// Exports
+module.exports = { requireEdition: requireEdition, requireEditions: requireEditions, requirePackage: requirePackage };

+ 169 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/package.json

@@ -0,0 +1,169 @@
+{
+  "_from": "editions@^1.1.1",
+  "_id": "editions@1.3.4",
+  "_inBundle": false,
+  "_integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==",
+  "_location": "/eachr/editions",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "editions@^1.1.1",
+    "name": "editions",
+    "escapedName": "editions",
+    "rawSpec": "^1.1.1",
+    "saveSpec": null,
+    "fetchSpec": "^1.1.1"
+  },
+  "_requiredBy": [
+    "/eachr"
+  ],
+  "_resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz",
+  "_shasum": "3662cb592347c3168eb8e498a0ff73271d67f50b",
+  "_spec": "editions@^1.1.1",
+  "_where": "E:\\nwjs-sdk-v0.34.2-win-x64\\node_modules\\eachr",
+  "author": {
+    "name": "2016+ Bevry Pty Ltd",
+    "email": "us@bevry.me",
+    "url": "http://bevry.me"
+  },
+  "badges": {
+    "list": [
+      "travisci",
+      "npmversion",
+      "npmdownloads",
+      "daviddm",
+      "daviddmdev",
+      "---",
+      "patreon",
+      "opencollective",
+      "gratipay",
+      "flattr",
+      "paypal",
+      "bitcoin",
+      "wishlist",
+      "---",
+      "slackin"
+    ],
+    "config": {
+      "patreonUsername": "bevry",
+      "opencollectiveUsername": "bevry",
+      "gratipayUsername": "bevry",
+      "flattrUsername": "balupton",
+      "paypalURL": "https://bevry.me/paypal",
+      "bitcoinURL": "https://bevry.me/bitcoin",
+      "wishlistURL": "https://bevry.me/wishlist",
+      "slackinURL": "https://slack.bevry.me"
+    }
+  },
+  "bugs": {
+    "url": "https://github.com/bevry/editions/issues"
+  },
+  "bundleDependencies": false,
+  "contributors": [
+    {
+      "name": "Benjamin Lupton",
+      "email": "b@lupton.cc",
+      "url": "http://balupton.com"
+    },
+    {
+      "name": "Zlatan Vasović",
+      "email": "zlatanvasovic@gmail.com",
+      "url": "http://zdroid.github.io"
+    }
+  ],
+  "dependencies": {},
+  "deprecated": false,
+  "description": "Publish multiple editions for your JavaScript packages consistently and easily (e.g. source edition, esnext edition, es2015 edition)",
+  "devDependencies": {
+    "assert-helpers": "^4.5.1",
+    "babel-cli": "^6.26.0",
+    "babel-preset-es2015": "^6.24.1",
+    "documentation": "^5.3.5",
+    "eslint": "^4.16.0",
+    "flow-bin": "^0.64.0",
+    "joe": "^2.0.2",
+    "joe-reporter-console": "^2.0.1",
+    "projectz": "^1.4.0",
+    "surge": "^0.19.0"
+  },
+  "editions": [
+    {
+      "description": "Source + ESNext + Require + Flow Type Comments",
+      "directory": "source",
+      "entry": "index.js",
+      "syntaxes": [
+        "javascript",
+        "esnext",
+        "require",
+        "arrows",
+        "destructuring",
+        "const",
+        "let",
+        "flow type comments"
+      ]
+    },
+    {
+      "description": "Babel Compiled + ES2015 + Require",
+      "directory": "es2015",
+      "entry": "index.js",
+      "syntaxes": [
+        "javascript",
+        "es2015",
+        "require"
+      ]
+    }
+  ],
+  "engines": {
+    "node": ">=0.8"
+  },
+  "homepage": "https://github.com/bevry/editions",
+  "keywords": [
+    "editions",
+    "edition",
+    "versions",
+    "syntaxes",
+    "esnext",
+    "jsnext",
+    "es2015",
+    "es6",
+    "es6+"
+  ],
+  "license": "MIT",
+  "main": "es2015/index.js",
+  "maintainers": [
+    {
+      "name": "Benjamin Lupton",
+      "email": "b@lupton.cc",
+      "url": "http://balupton.com"
+    }
+  ],
+  "name": "editions",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/bevry/editions.git"
+  },
+  "scripts": {
+    "our:clean": "rm -Rf ./docs ./es2015 ./es5 ./out",
+    "our:compile": "npm run our:compile:es2015",
+    "our:compile:es2015": "babel ./source --out-dir ./es2015 --presets es2015",
+    "our:meta": "npm run our:meta:docs && npm run our:meta:projectz",
+    "our:meta:docs": "documentation build -f html -o ./docs -g --shallow ./source/**.js",
+    "our:meta:projectz": "projectz compile",
+    "our:release": "npm run our:release:prepare && npm run our:release:check && npm run our:release:tag && npm run our:release:push",
+    "our:release:check": "npm run our:release:check:changelog && npm run our:release:check:dirty",
+    "our:release:check:changelog": "cat ./HISTORY.md | grep v$npm_package_version || (echo add a changelog entry for v$npm_package_version && exit -1)",
+    "our:release:check:dirty": "git diff --exit-code",
+    "our:release:prepare": "npm run our:clean && npm run our:compile && npm run our:test && npm run our:meta",
+    "our:release:push": "git push origin master && git push origin --tags",
+    "our:release:tag": "export MESSAGE=$(cat ./HISTORY.md | sed -n \"/## v$npm_package_version/,/##/p\" | sed 's/## //' | awk 'NR>1{print buf}{buf = $0}') && test \"$MESSAGE\" || (echo 'proper changelog entry not found' && exit -1) && git tag v$npm_package_version -am \"$MESSAGE\"",
+    "our:setup": "npm run our:setup:npm",
+    "our:setup:npm": "npm install",
+    "our:test": "npm run our:verify && npm test",
+    "our:verify": "npm run our:verify:eslint && npm run our:verify:flow",
+    "our:verify:eslint": "eslint --fix ./source",
+    "our:verify:flow": "flow check",
+    "test": "node --harmony ./es2015/test.js --joe-reporter=console"
+  },
+  "version": "1.3.4"
+}

+ 190 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/node_modules/editions/source/index.js

@@ -0,0 +1,190 @@
+/* @flow */
+/* eslint no-console:0 */
+'use strict'
+
+// Imports
+const pathUtil = require('path')
+
+// Helper class to display nested error in a sensible way
+class DetailedError extends Error {
+	constructor (message /* :string */, details /* :Object */) {
+		Object.keys(details).forEach(function (key) {
+			const data = details[key]
+			const value = require('util').inspect(data.stack || data.message || data)
+			message += `\n${key}: ${value}`
+		})
+		super(message)
+	}
+}
+
+// Environment fetching
+const blacklist = process && process.env && process.env.EDITIONS_SYNTAX_BLACKLIST && process.env.EDITIONS_SYNTAX_BLACKLIST.split(',')
+
+// Cache of which syntax combinations are supported or unsupported, hash of booleans
+const syntaxFailedCombitions = {}  // sorted lowercase syntax combination => Error instance of failure
+const syntaxBlacklist = {}
+syntaxBlacklist.import = new Error('The import syntax is skipped as the module package.json field eliminates the need for autoloader support')
+syntaxBlacklist.coffeescript = new Error('The coffeescript syntax is skipped as we want to use a precompiled edition rather than compiling at runtime')
+syntaxBlacklist.typescript = new Error('The typescript syntax is skipped as we want to use a precompiled edition rather than compiling at runtime')
+
+// Blacklist non-esnext node versions from esnext
+if ( process && process.versions && process.versions.node ) {
+	const EARLIEST_ESNEXT_NODE_VERSION = [0, 12]
+	const NODE_VERSION = process.versions.node.split('.').map((n) => parseInt(n, 10))
+	const ESNEXT_UNSUPPORTED = NODE_VERSION[0] < EARLIEST_ESNEXT_NODE_VERSION[0] || (
+		NODE_VERSION[0] === EARLIEST_ESNEXT_NODE_VERSION[0] &&
+		NODE_VERSION[1] < EARLIEST_ESNEXT_NODE_VERSION[1]
+	)
+	if ( ESNEXT_UNSUPPORTED )  syntaxBlacklist.esnext = new Error('The esnext syntax is skipped on early node versions as attempting to use esnext features will output debugging information on these node versions')
+}
+
+// Check the environment configuration for a syntax blacklist
+if ( blacklist ) {
+	for ( let i = 0; i < blacklist.length; ++i ) {
+		const syntax = blacklist[i].trim().toLowerCase()
+		syntaxBlacklist[syntax] = new DetailedError('The EDITIONS_SYNTAX_BLACKLIST environment variable has blacklisted an edition syntax:', {syntax, blacklist})
+	}
+}
+
+/* ::
+type edition = {
+	name:number,
+	description?:string,
+	directory?:string,
+	entry?:string,
+	syntaxes?:Array<string>
+};
+type options = {
+	cwd?:string,
+	package?:string,
+	entry?:string,
+	require:function
+};
+*/
+
+/**
+ * Cycle through the editions and require the correct one
+ * @protected internal function that is untested for public consumption
+ * @param {edition} edition - the edition entry
+ * @param {Object} opts - the following options
+ * @param {string} opts.require - the require method of the calling module, used to ensure require paths remain correct
+ * @param {string} [opts.cwd] - if provided, this will be the cwd for entries
+ * @param {string} [opts.entry] - if provided, should be a relative or absolute path to the entry point of the edition
+ * @param {string} [opts.package] - if provided, should be the name of the package that we are loading the editions for
+ * @returns {*}
+ */
+function requireEdition ( edition /* :edition */, opts /* :options */ ) /* :any */ {
+	// Prevent require from being included in debug logs
+	Object.defineProperty(opts, 'require', {value: opts.require, enumerable: false})
+
+	// Get the correct entry path
+	// As older versions o
+	const cwd = opts.cwd || ''
+	const dir = edition.directory || ''
+	let entry = opts.entry || edition.entry || ''
+	if ( dir && entry && entry.indexOf(dir + '/') === 0 )  entry = entry.substring(dir.length + 1)
+	// ^ this should not be needed, but as previous versions of editions included the directory inside the entry
+	// it unfortunately is, as such this is a stepping stone for the new format, the new format being
+	// if entry is specified by itself, it is cwd => entry
+	// if entry is specified with a directory, it is cwd => dir => entry
+	// if entry is not specified but dir is, it is cwd => dir
+	// if neither entry nor dir are specified, we have a problem
+	if ( !dir && !entry ) {
+		const editionFailure = new DetailedError('Skipped edition due to no entry or directory being specified:', {edition, cwd, dir, entry})
+		throw editionFailure
+	}
+	const entryPath = pathUtil.resolve(cwd, dir, entry)
+
+	// Check syntax support
+	// Convert syntaxes into a sorted lowercase string
+	const syntaxes = edition.syntaxes && edition.syntaxes.map((i) => i.toLowerCase()).sort()
+	const syntaxCombination = syntaxes && syntaxes.join(', ')
+	if ( syntaxes && syntaxCombination ) {
+		// Check if any of the syntaxes are unsupported
+		const unsupportedSyntaxes = syntaxes.filter((i) => syntaxBlacklist[i.toLowerCase()])
+		if ( unsupportedSyntaxes.length ) {
+			const editionFailure = new DetailedError('Skipped edition due to it containing an unsupported syntax:', {edition, unsupportedSyntaxes})
+			throw editionFailure
+		}
+		// Is this syntax combination unsupported? If so skip it with a soft failure to try the next edition
+		else if ( syntaxFailedCombitions[syntaxCombination] ) {
+			const previousCombinationFailure = syntaxFailedCombitions[syntaxCombination]
+			const editionFailure = new DetailedError('Skipped edition due to its syntax combinatiom failing previously:', {edition, previousCombinationFailure})
+			throw editionFailure
+		}
+	}
+
+	// Try and load this syntax combination
+	try {
+		return opts.require(entryPath)
+	}
+	catch ( error ) {
+		// Note the error with more details
+		const editionFailure = new DetailedError('Failed to load the edition due to a load error:', {edition, error: error.stack})
+
+		// Blacklist the combination, even if it may have worked before
+		// Perhaps in the future note if that if it did work previously, then we should instruct module owners to be more specific with their syntaxes
+		if ( syntaxCombination )  syntaxFailedCombitions[syntaxCombination] = editionFailure
+
+		// Continue to the next edition
+		throw editionFailure
+	}
+}
+
+/**
+ * Cycle through the editions and require the correct one
+ * @protected internal function that is untested for public consumption
+ * @param {Array<edition>} editions - an array of edition entries
+ * @param {Object} opts - the following options
+ * @param {string} opts.require - the require method of the calling module, used to ensure require paths remain correct
+ * @param {string} [opts.cwd] - if provided, this will be the cwd for entries
+ * @param {string} [opts.entry] - if provided, should be a relative path to the entry point of the edition
+ * @param {string} [opts.package] - if provided, should be the name of the package that we are loading the editions for
+ * @returns {*}
+ */
+function requireEditions ( editions /* :Array<edition> */, opts /* :options */ ) /* :any */ {
+	// Extract
+	if ( opts.package == null )  opts.package = 'custom runtime package'
+
+	// Check
+	if ( !editions || editions.length === 0 ) {
+		throw new DetailedError('No editions were specified:', {opts})
+	}
+
+	// Note the last error message
+	const editionFailures = []
+
+	// Cycle through the editions
+	for ( let i = 0; i < editions.length; ++i ) {
+		const edition = editions[i]
+		try {
+			return requireEdition(edition, opts)
+		}
+		catch ( err ) {
+			editionFailures.push(err)
+		}
+	}
+
+	// Through the error as no edition loaded
+	throw new DetailedError('There are no suitable editions for this environment:', {opts, editions, failures: editionFailures})
+}
+
+/**
+ * Cycle through the editions for a package and require the correct one
+ * @param {string} cwd - the path of the package, used to load package.json:editions and handle relative edition entry points
+ * @param {function} require - the require method of the calling module, used to ensure require paths remain correct
+ * @param {string} [entry] - an optional override for the entry of an edition, requires the edition to specify a `directory` property
+ * @returns {*}
+ */
+function requirePackage (cwd /* :string */, require /* :function */, entry /* :: ?:string */ ) /* :any */ {
+	// Load the package.json file to fetch `name` for debugging and `editions` for loading
+	const packagePath = pathUtil.resolve(cwd, 'package.json')
+	const {name, editions} = require(packagePath)
+	const opts /* :options */ = {cwd, require}
+	if ( name )  opts.package = name
+	if ( entry )  opts.entry = entry
+	return requireEditions(editions, opts)
+}
+
+// Exports
+module.exports = {requireEdition, requireEditions, requirePackage}

+ 172 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/package.json

@@ -0,0 +1,172 @@
+{
+  "_from": "eachr@^3.2.0",
+  "_id": "eachr@3.2.0",
+  "_inBundle": false,
+  "_integrity": "sha1-LDXkPqCGUW95l8+At6pk1VpKRIQ=",
+  "_location": "/eachr",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "eachr@^3.2.0",
+    "name": "eachr",
+    "escapedName": "eachr",
+    "rawSpec": "^3.2.0",
+    "saveSpec": null,
+    "fetchSpec": "^3.2.0"
+  },
+  "_requiredBy": [
+    "/extract-opts"
+  ],
+  "_resolved": "https://registry.npmjs.org/eachr/-/eachr-3.2.0.tgz",
+  "_shasum": "2c35e43ea086516f7997cf80b7aa64d55a4a4484",
+  "_spec": "eachr@^3.2.0",
+  "_where": "E:\\nwjs-sdk-v0.34.2-win-x64\\node_modules\\extract-opts",
+  "author": {
+    "name": "2011+ Bevry Pty Ltd",
+    "email": "us@bevry.me",
+    "url": "http://bevry.me"
+  },
+  "badges": {
+    "list": [
+      "travisci",
+      "npmversion",
+      "npmdownloads",
+      "daviddm",
+      "daviddmdev",
+      "---",
+      "slackin",
+      "patreon",
+      "gratipay",
+      "flattr",
+      "paypal",
+      "bitcoin",
+      "wishlist"
+    ],
+    "config": {
+      "patreonUsername": "bevry",
+      "gratipayUsername": "bevry",
+      "flattrUsername": "balupton",
+      "paypalURL": "https://bevry.me/paypal",
+      "bitcoinURL": "https://bevry.me/bitcoin",
+      "wishlistURL": "https://bevry.me/wishlist",
+      "slackinURL": "https://slack.bevry.me"
+    }
+  },
+  "browser": "es2015/index.js",
+  "bugs": {
+    "url": "https://github.com/bevry/eachr/issues"
+  },
+  "bundleDependencies": false,
+  "contributors": [
+    {
+      "name": "Benjamin Lupton",
+      "email": "b@lupton.cc",
+      "url": "http://balupton.com"
+    },
+    {
+      "name": "Sean Fridman",
+      "email": "fridman@mail.sfsu.edu",
+      "url": "www.seanfridman.com"
+    },
+    {
+      "name": "Rob Loach",
+      "email": "robloach@gmail.com",
+      "url": "http://robloach.net"
+    },
+    {
+      "name": "Sean Fridman",
+      "email": "mail@seanfridman.com",
+      "url": "http://seanfridman.com"
+    },
+    {
+      "name": "Benjamin Lupton",
+      "url": "https://balupton.com"
+    }
+  ],
+  "dependencies": {
+    "editions": "^1.1.1",
+    "typechecker": "^4.3.0"
+  },
+  "deprecated": false,
+  "description": "Give eachr an item to iterate (array, object or map) and an iterator, then in return eachr gives iterator the value and key of each item, and will stop if the iterator returned false.",
+  "devDependencies": {
+    "assert-helpers": "^4.2.0",
+    "babel-cli": "^6.9.0",
+    "babel-preset-es2015": "^6.9.0",
+    "eslint": "^2.10.2",
+    "eslint-plugin-babel": "^3.2.0",
+    "joe": "^1.6.0",
+    "joe-reporter-console": "^1.2.1",
+    "projectz": "^1.1.5",
+    "semver": "^5.1.0"
+  },
+  "editions": [
+    {
+      "description": "Source + ESNext + Require",
+      "entry": "source/index.js",
+      "directory": "source",
+      "syntaxes": [
+        "javascript",
+        "esnext",
+        "require",
+        "let",
+        "const"
+      ]
+    },
+    {
+      "description": "Babel Compiled + ES2015 + Require",
+      "entry": "es2015/index.js",
+      "directory": "es2015",
+      "syntaxes": [
+        "javascript",
+        "es2015",
+        "require"
+      ]
+    }
+  ],
+  "engines": {
+    "node": ">=0.10"
+  },
+  "homepage": "https://github.com/bevry/eachr",
+  "keywords": [
+    "flow",
+    "each",
+    "cycle",
+    "forEach",
+    "map",
+    "object",
+    "array"
+  ],
+  "license": "MIT",
+  "main": "index.js",
+  "maintainers": [
+    {
+      "name": "Benjamin Lupton",
+      "email": "b@lupton.cc",
+      "url": "http://balupton.com"
+    }
+  ],
+  "name": "eachr",
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/bevry/eachr.git"
+  },
+  "scripts": {
+    "clean": "rm -Rf ./docs ./es2015",
+    "compile": "npm run compile:es2015",
+    "compile:es2015": "babel ./source --out-dir ./es2015 --presets es2015",
+    "meta": "npm run meta:projectz",
+    "meta:projectz": "projectz compile",
+    "prepare": "npm run compile && npm run test && npm run meta",
+    "pretest": "npm run test:eslint",
+    "release": "npm run prepare && npm run release:publish && npm run release:tag && npm run release:push",
+    "release:publish": "npm publish",
+    "release:push": "git push origin master && git push origin --tags",
+    "release:tag": "git tag v$npm_package_version -a",
+    "setup": "npm install",
+    "test": "node --harmony -e \"require('editions').requirePackage(process.cwd(), require, 'test.js')\"",
+    "test:eslint": "eslint ./source"
+  },
+  "version": "3.2.0"
+}

+ 43 - 0
src/jfw/modules/pc/client/apple/node_modules/eachr/source/index.js

@@ -0,0 +1,43 @@
+/* eslint no-cond-assign:0 */
+
+// Import
+const typeChecker = require('typechecker')
+
+// Eachr
+module.exports = function eachr (subject, callback) {
+	// Handle
+	if ( typeChecker.isArray(subject) ) {
+		for ( let key = 0; key < subject.length; ++key ) {
+			const value = subject[key]
+			if ( callback.call(subject, value, key, subject) === false ) {
+				break
+			}
+		}
+	}
+	else if ( typeChecker.isPlainObject(subject) ) {
+		for ( const key in subject ) {
+			if ( subject.hasOwnProperty(key) ) {
+				const value = subject[key]
+				if ( callback.call(subject, value, key, subject) === false ) {
+					break
+				}
+			}
+		}
+	}
+	else if ( typeChecker.isMap(subject) ) {
+		const entries = subject.entries()
+		let entry; while ( entry = entries.next().value ) {
+			const [key, value] = entry  // destructuring
+			if ( callback.call(subject, value, key, subject) === false ) {
+				break
+			}
+		}
+	}
+	else {
+		// Perhaps falling back to a `for of` loop here would be sensible
+		throw new Error('eachr does not know how to iterate what was passed to it')
+	}
+
+	// Return
+	return subject
+}

+ 64 - 0
src/jfw/modules/pc/client/apple/node_modules/editions/HISTORY.md

@@ -0,0 +1,64 @@
+# History
+
+## v2.0.2 2018 September 3
+- Fixed `Error: Cannot find module 'editions'` on Windows (caused by edition directories containing `:` which is unsupported on Windows)
+    - Regression in v2.0.0
+    - Closes
+        - [ungit issue #1130](https://github.com/FredrikNoren/ungit/issues/1130)
+        - [getmac issue #39](https://github.com/bevry/getmac/issues/39)
+        - [docpad issue #1088](https://github.com/docpad/docpad/issues/1088)
+        - [bevry thread #240](https://discuss.bevry.me/t/error-cannot-find-module-editions/240)
+
+## v2.0.1 2018 August 24
+- Fixed potential `Error: Cannot find module 'editions'` (causes by `main` pointing to a legacy location
+    - Regression in v2.0.0
+- Added an edition for browsers
+
+## v2.0.0 2018 July 27
+- Edition entries must now make use of the fields: `description`, `directory`, `entry`, and the new `engines` field (which follows the [`package.json:engines` spec](https://docs.npmjs.com/files/package.json#engines)).
+- In version 1, if an edition failed to load, its syntax combination would be blacklisted. This functionality has been removed. The `engines` field is a better replacement. The `syntaxes` field remains optional, as it is still useful for user configured blacklisting and ecosystem tooling.
+- Errors reported by the autoloader have improved readability thanks to [Errlop](https://github.com/bevry/errlop)
+- Updated base files
+
+## v1.3.4 2018 January 31
+- Updated base files
+
+## v1.3.3 2016 November 4
+- Properly add node 0.8 support
+
+## v1.3.2 2016 November 4
+- Added node 0.8 support
+
+## v1.3.1 2016 October 11
+- Fixed failure to load editions that had the edition directory within the edition entry
+  - Thanks to [Jordan Harband](https://github.com/ljharb) for [issue #20](https://github.com/bevry/editions/issues/20)
+
+## v1.3.0 2016 October 11
+- Added support for `EDITIONS_SYNTAX_BLACKLIST` environment variable
+  - Thanks to [Damon Maria](https://github.com/damonmaria) for [issue #10](https://github.com/bevry/editions/issues/10)
+- Dropped need for `DEBUG_BEVRY_EDITIONS` as failures will not output all the necessary debugging information
+
+## v1.2.1 2016 October 10
+- Change `esnext` skip from v8 engines < 4 to node engines < 0.12
+
+## v1.2.0 2016 October 10
+- Skip syntaxes that require preprocessors
+- Skip `import` syntax, as the `module` field inside `package.json` skips the autoloader if supported
+- Skip `esnext` syntax on v8 engines < 4
+
+## v1.1.2 2016 June 16
+- Parent errors are now displayed in a more sensible way
+
+## v1.1.1 2016 March 20
+- Errors and debug messages are now more useful
+  - Closes https://github.com/bevry/editions/issues/5
+
+## v1.1.0 2016 March 20
+- Added support for custom entry point overrides
+- Debugging goes to `console.error` (stderr) rather than `console.log` (stdout)
+  - Closes https://github.com/bevry/editions/issues/2
+- Added tests
+  - Closes https://github.com/bevry/editions/issues/4
+
+## v1.0.1 2016 March 9
+- Initial release

+ 23 - 0
src/jfw/modules/pc/client/apple/node_modules/editions/LICENSE.md

@@ -0,0 +1,23 @@
+<!-- LICENSEFILE/ -->
+
+<h1>License</h1>
+
+Unless stated otherwise all works are:
+
+<ul><li>Copyright &copy; 2016+ <a href="http://bevry.me">Bevry Pty Ltd</a></li></ul>
+
+and licensed under:
+
+<ul><li><a href="http://spdx.org/licenses/MIT.html">MIT License</a></li></ul>
+
+<h2>MIT License</h2>
+
+<pre>
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+</pre>
+
+<!-- /LICENSEFILE -->

+ 105 - 0
src/jfw/modules/pc/client/apple/node_modules/editions/README.md

@@ -0,0 +1,105 @@
+<!-- TITLE/ -->
+
+<h1>editions</h1>
+
+<!-- /TITLE -->
+
+
+<!-- BADGES/ -->
+
+<span class="badge-travisci"><a href="http://travis-ci.org/bevry/editions" title="Check this project's build status on TravisCI"><img src="https://img.shields.io/travis/bevry/editions/master.svg" alt="Travis CI Build Status" /></a></span>
+<span class="badge-npmversion"><a href="https://npmjs.org/package/editions" title="View this project on NPM"><img src="https://img.shields.io/npm/v/editions.svg" alt="NPM version" /></a></span>
+<span class="badge-npmdownloads"><a href="https://npmjs.org/package/editions" title="View this project on NPM"><img src="https://img.shields.io/npm/dm/editions.svg" alt="NPM downloads" /></a></span>
+<span class="badge-daviddm"><a href="https://david-dm.org/bevry/editions" title="View the status of this project's dependencies on DavidDM"><img src="https://img.shields.io/david/bevry/editions.svg" alt="Dependency Status" /></a></span>
+<span class="badge-daviddmdev"><a href="https://david-dm.org/bevry/editions#info=devDependencies" title="View the status of this project's development dependencies on DavidDM"><img src="https://img.shields.io/david/dev/bevry/editions.svg" alt="Dev Dependency Status" /></a></span>
+<br class="badge-separator" />
+<span class="badge-patreon"><a href="https://patreon.com/bevry" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
+<span class="badge-opencollective"><a href="https://opencollective.com/bevry" title="Donate to this project using Open Collective"><img src="https://img.shields.io/badge/open%20collective-donate-yellow.svg" alt="Open Collective donate button" /></a></span>
+<span class="badge-flattr"><a href="https://flattr.com/profile/balupton" title="Donate to this project using Flattr"><img src="https://img.shields.io/badge/flattr-donate-yellow.svg" alt="Flattr donate button" /></a></span>
+<span class="badge-paypal"><a href="https://bevry.me/paypal" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" alt="PayPal donate button" /></a></span>
+<span class="badge-bitcoin"><a href="https://bevry.me/bitcoin" title="Donate once-off to this project using Bitcoin"><img src="https://img.shields.io/badge/bitcoin-donate-yellow.svg" alt="Bitcoin donate button" /></a></span>
+<span class="badge-wishlist"><a href="https://bevry.me/wishlist" title="Buy an item on our wishlist for us"><img src="https://img.shields.io/badge/wishlist-donate-yellow.svg" alt="Wishlist browse button" /></a></span>
+
+<!-- /BADGES -->
+
+
+<!-- DESCRIPTION/ -->
+
+Publish multiple editions for your JavaScript packages consistently and easily (e.g. source edition, esnext edition, es2015 edition)
+
+<!-- /DESCRIPTION -->
+
+
+## Discover
+
+[Watch the talk.](https://youtu.be/IAB8_UlcNWI)
+
+[Get started with the guides and explanations.](https://github.com/bevry/editions/wiki)
+
+[View the API documentation.](http://master.editions.bevry.surge.sh/docs/)
+
+
+<!-- HISTORY/ -->
+
+<h2>History</h2>
+
+<a href="https://github.com/bevry/editions/blob/master/HISTORY.md#files">Discover the release history by heading on over to the <code>HISTORY.md</code> file.</a>
+
+<!-- /HISTORY -->
+
+
+<!-- CONTRIBUTE/ -->
+
+<h2>Contribute</h2>
+
+<a href="https://github.com/bevry/editions/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a>
+
+<!-- /CONTRIBUTE -->
+
+
+<!-- BACKERS/ -->
+
+<h2>Backers</h2>
+
+<h3>Maintainers</h3>
+
+These amazing people are maintaining this project:
+
+<ul><li><a href="http://balupton.com">Benjamin Lupton</a> — <a href="https://github.com/bevry/editions/commits?author=balupton" title="View the GitHub contributions of Benjamin Lupton on repository bevry/editions">view contributions</a></li></ul>
+
+<h3>Sponsors</h3>
+
+No sponsors yet! Will you be the first?
+
+<span class="badge-patreon"><a href="https://patreon.com/bevry" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
+<span class="badge-opencollective"><a href="https://opencollective.com/bevry" title="Donate to this project using Open Collective"><img src="https://img.shields.io/badge/open%20collective-donate-yellow.svg" alt="Open Collective donate button" /></a></span>
+<span class="badge-flattr"><a href="https://flattr.com/profile/balupton" title="Donate to this project using Flattr"><img src="https://img.shields.io/badge/flattr-donate-yellow.svg" alt="Flattr donate button" /></a></span>
+<span class="badge-paypal"><a href="https://bevry.me/paypal" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" alt="PayPal donate button" /></a></span>
+<span class="badge-bitcoin"><a href="https://bevry.me/bitcoin" title="Donate once-off to this project using Bitcoin"><img src="https://img.shields.io/badge/bitcoin-donate-yellow.svg" alt="Bitcoin donate button" /></a></span>
+<span class="badge-wishlist"><a href="https://bevry.me/wishlist" title="Buy an item on our wishlist for us"><img src="https://img.shields.io/badge/wishlist-donate-yellow.svg" alt="Wishlist browse button" /></a></span>
+
+<h3>Contributors</h3>
+
+These amazing people have contributed code to this project:
+
+<ul><li><a href="http://balupton.com">Benjamin Lupton</a> — <a href="https://github.com/bevry/editions/commits?author=balupton" title="View the GitHub contributions of Benjamin Lupton on repository bevry/editions">view contributions</a></li>
+<li><a href="http://zdroid.github.io">Zlatan Vasović</a> — <a href="https://github.com/bevry/editions/commits?author=zdroid" title="View the GitHub contributions of Zlatan Vasović on repository bevry/editions">view contributions</a></li></ul>
+
+<a href="https://github.com/bevry/editions/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a>
+
+<!-- /BACKERS -->
+
+
+<!-- LICENSE/ -->
+
+<h2>License</h2>
+
+Unless stated otherwise all works are:
+
+<ul><li>Copyright &copy; 2016+ <a href="http://bevry.me">Bevry Pty Ltd</a></li></ul>
+
+and licensed under:
+
+<ul><li><a href="http://spdx.org/licenses/MIT.html">MIT License</a></li></ul>
+
+<!-- /LICENSE -->

+ 188 - 0
src/jfw/modules/pc/client/apple/node_modules/editions/edition-browsers/index.js

@@ -0,0 +1,188 @@
+/* eslint no-console:0 */
+'use strict';
+
+/**
+ * @typedef {Object} Edition
+ * @property {string} description
+ * @property {string} directory
+ * @property {string} entry
+ * @property {Array<string>} syntaxes
+ * @property {false|Object.<string, string|boolean>} engines
+ * @public
+ */
+
+/**
+ * @typedef {Object} Options
+ * @property {Function} require - the require method of the calling module, used to ensure require paths remain correct
+ * @property {string} [packagePath] - if provided, this is used for debugging
+ * @property {boolean} [verbose] - if provided, any error loading an edition will be logged. By default, errors are only logged if all editions failed. If not provided, process.env.EDITIONS_VERBOSE is used.
+ * @property {string} [cwd] - if provided, this will be the cwd for entries
+ * @property {string} [entry] - if provided, should be a relative path to the entry point of the edition
+ * @property {string} [package] - if provided, should be the name of the package that we are loading the editions for
+ * @property {stream.Writable} [stderr] - if not provided, will use process.stderr instead. It is the stream that verbose errors are logged to.
+ * @public
+ */
+
+// Imports
+
+var pathUtil = require('path');
+var semver = require('semver');
+var Errlop = require('errlop');
+
+// Helpers
+function stringify(value) {
+	return typeof value === 'string' ? value : JSON.stringify(value);
+}
+
+// Environment fetching
+var BLACKLIST = process.env.EDITIONS_SYNTAX_BLACKLIST && process.env.EDITIONS_SYNTAX_BLACKLIST.split(/[, ]+/g);
+var NODE_VERSION = process.versions.node;
+var VERBOSE = process.env.EDITIONS_VERBOSE === true || process.env.EDITIONS_VERBOSE === 'yes' || process.env.EDITIONS_VERBOSE === 'true' || false;
+
+// Cache of which syntax combinations are supported or unsupported
+// Object.<string, Error>
+var blacklist = {};
+
+// Check the environment configuration for a syntax blacklist
+if (BLACKLIST) {
+	for (var i = 0; i < BLACKLIST.length; ++i) {
+		var syntax = BLACKLIST[i].trim().toLowerCase();
+		blacklist[syntax] = new Errlop('The EDITIONS_SYNTAX_BLACKLIST environment variable blacklisted the syntax [' + syntax + ']');
+	}
+}
+
+// Blacklist the syntax 'esnext' if our node version is below 0.12
+if (semver.satisfies(NODE_VERSION, '<0.12')) {
+	blacklist.esnext = new Error('The esnext syntax is skipped on early node versions as attempting to use esnext features will output debugging information on these node versions');
+}
+
+/**
+ * Attempt to load a specific edition
+ * @param {Edition} edition
+ * @param {Options} opts
+ * @returns {*} the result of the loaded edition
+ * @throws {Error} an error if the edition failed to load
+ * @public
+ */
+function requireEdition(edition, opts) {
+	// Verify the edition is valid
+	if (!edition.description || !edition.directory || !edition.entry || edition.engines == null) {
+		var editionInvalidError = new Errlop('Each edition must have its [description, directory, entry, engines] fields defined, yet all it had was [' + Object.keys(edition).join(', ') + ']');
+		editionInvalidError.level = 'fatal';
+		throw editionInvalidError;
+	}
+
+	// Verify engine support
+	if (edition.engines === false) {
+		throw new Errlop('Skipping edition [' + edition.description + '] because its engines field was false');
+	}
+	if (!edition.engines.node) {
+		throw new Errlop('Skipping edition [' + edition.description + '] because its .engines.node field was falsey');
+	}
+	if (edition.engines.node !== true && semver.satisfies(NODE_VERSION, edition.engines.node) === false) {
+		throw new Errlop('Skipping edition [' + edition.description + '] because our current node version [' + NODE_VERSION + '] is not supported by its [' + stringify(edition.engines.node) + ']');
+	}
+
+	// Verify syntax support
+	// Convert syntaxes into a sorted lowercase string
+	var syntaxes = edition.syntaxes && edition.syntaxes.map(function (i) {
+		return i.toLowerCase();
+	}).sort() || [];
+	for (var index = 0; index < syntaxes.length; index++) {
+		var _syntax = syntaxes[index];
+		var blacklisted = blacklist[_syntax];
+		if (blacklisted) {
+			throw new Errlop('Skipping edition [' + edition.description + '] because it contained a blacklisted syntax [' + _syntax + ']', blacklisted);
+		}
+	}
+
+	// Load the edition
+	var entry = pathUtil.resolve(opts.cwd || '', edition.directory, opts.entry || edition.entry);
+	try {
+		return opts.require(entry);
+	} catch (loadError) {
+		// Note the error with more details
+		throw new Errlop('Skipped edition [' + edition.description + '] at entry [' + entry + '] because it failed to load', loadError);
+	}
+}
+
+/**
+ * Cycles through a list of editions, returning the first suitable edition that it was able to load
+ * @param {Array<Edition>} editions
+ * @param {Options} opts
+ * @returns {*} the result of the loaded edition
+ * @throws {Error} an error if a suitable edition was unable to be resolved
+ * @public
+ */
+function requireEditions(editions, opts) {
+	// Check
+	if (!editions || editions.length === 0) {
+		if (opts.packagePath) {
+			throw new Errlop('There were no editions specified for package [' + opts.packagePath + ']');
+		} else {
+			throw new Errlop('There were no editions specified');
+		}
+	}
+
+	// Note the last error message
+	var result = void 0,
+	    editionsError = null,
+	    loaded = false;
+
+	// Cycle through the editions
+	for (var _i = 0; _i < editions.length; ++_i) {
+		var edition = editions[_i];
+		try {
+			result = requireEdition(edition, opts);
+			loaded = true;
+			break;
+		} catch (editionError) {
+			if (editionError.level === 'fatal') {
+				editionsError = editionError;
+				break;
+			} else if (editionsError) {
+				editionsError = new Errlop(editionsError, editionError);
+			} else {
+				editionsError = editionError;
+			}
+		}
+	}
+
+	if (loaded) {
+		var verbose = opts.verbose == null ? VERBOSE : opts.verbose;
+		if (editionsError && verbose) {
+			var stderr = opts.stderr || process.stderr;
+			stderr.write(editionsError.stack + '\n');
+		}
+		return result;
+	} else if (editionsError) {
+		if (opts.packagePath) {
+			throw new Errlop('There were no suitable editions for package [' + opts.packagePath + ']', editionsError);
+		} else {
+			throw new Errlop('There were no suitable editions', editionsError);
+		}
+	}
+}
+
+/**
+ * Cycle through the editions for a package and require the correct one
+ * @param {Options.cwd} cwd
+ * @param {Options.require} require
+ * @param {Options.entry} [entry]
+ * @returns {*} the result of the loaded edition
+ * @throws {Error} an error if a suitable edition was unable to be resolved
+ * @public
+ */
+function requirePackage(cwd, require, entry) {
+	// Load the package.json file to fetch `name` for debugging and `editions` for loading
+	var packagePath = pathUtil.resolve(cwd, 'package.json');
+
+	var _require = require(packagePath),
+	    editions = _require.editions;
+
+	var opts = { packagePath: packagePath, cwd: cwd, require: require, entry: entry };
+	return requireEditions(editions, opts);
+}
+
+// Exports
+module.exports = { requireEdition: requireEdition, requireEditions: requireEditions, requirePackage: requirePackage };

+ 188 - 0
src/jfw/modules/pc/client/apple/node_modules/editions/edition-node-0.8/index.js

@@ -0,0 +1,188 @@
+/* eslint no-console:0 */
+'use strict';
+
+/**
+ * @typedef {Object} Edition
+ * @property {string} description
+ * @property {string} directory
+ * @property {string} entry
+ * @property {Array<string>} syntaxes
+ * @property {false|Object.<string, string|boolean>} engines
+ * @public
+ */
+
+/**
+ * @typedef {Object} Options
+ * @property {Function} require - the require method of the calling module, used to ensure require paths remain correct
+ * @property {string} [packagePath] - if provided, this is used for debugging
+ * @property {boolean} [verbose] - if provided, any error loading an edition will be logged. By default, errors are only logged if all editions failed. If not provided, process.env.EDITIONS_VERBOSE is used.
+ * @property {string} [cwd] - if provided, this will be the cwd for entries
+ * @property {string} [entry] - if provided, should be a relative path to the entry point of the edition
+ * @property {string} [package] - if provided, should be the name of the package that we are loading the editions for
+ * @property {stream.Writable} [stderr] - if not provided, will use process.stderr instead. It is the stream that verbose errors are logged to.
+ * @public
+ */
+
+// Imports
+
+var pathUtil = require('path');
+var semver = require('semver');
+var Errlop = require('errlop');
+
+// Helpers
+function stringify(value) {
+	return typeof value === 'string' ? value : JSON.stringify(value);
+}
+
+// Environment fetching
+var BLACKLIST = process.env.EDITIONS_SYNTAX_BLACKLIST && process.env.EDITIONS_SYNTAX_BLACKLIST.split(/[, ]+/g);
+var NODE_VERSION = process.versions.node;
+var VERBOSE = process.env.EDITIONS_VERBOSE === true || process.env.EDITIONS_VERBOSE === 'yes' || process.env.EDITIONS_VERBOSE === 'true' || false;
+
+// Cache of which syntax combinations are supported or unsupported
+// Object.<string, Error>
+var blacklist = {};
+
+// Check the environment configuration for a syntax blacklist
+if (BLACKLIST) {
+	for (var i = 0; i < BLACKLIST.length; ++i) {
+		var syntax = BLACKLIST[i].trim().toLowerCase();
+		blacklist[syntax] = new Errlop('The EDITIONS_SYNTAX_BLACKLIST environment variable blacklisted the syntax [' + syntax + ']');
+	}
+}
+
+// Blacklist the syntax 'esnext' if our node version is below 0.12
+if (semver.satisfies(NODE_VERSION, '<0.12')) {
+	blacklist.esnext = new Error('The esnext syntax is skipped on early node versions as attempting to use esnext features will output debugging information on these node versions');
+}
+
+/**
+ * Attempt to load a specific edition
+ * @param {Edition} edition
+ * @param {Options} opts
+ * @returns {*} the result of the loaded edition
+ * @throws {Error} an error if the edition failed to load
+ * @public
+ */
+function requireEdition(edition, opts) {
+	// Verify the edition is valid
+	if (!edition.description || !edition.directory || !edition.entry || edition.engines == null) {
+		var editionInvalidError = new Errlop('Each edition must have its [description, directory, entry, engines] fields defined, yet all it had was [' + Object.keys(edition).join(', ') + ']');
+		editionInvalidError.level = 'fatal';
+		throw editionInvalidError;
+	}
+
+	// Verify engine support
+	if (edition.engines === false) {
+		throw new Errlop('Skipping edition [' + edition.description + '] because its engines field was false');
+	}
+	if (!edition.engines.node) {
+		throw new Errlop('Skipping edition [' + edition.description + '] because its .engines.node field was falsey');
+	}
+	if (edition.engines.node !== true && semver.satisfies(NODE_VERSION, edition.engines.node) === false) {
+		throw new Errlop('Skipping edition [' + edition.description + '] because our current node version [' + NODE_VERSION + '] is not supported by its [' + stringify(edition.engines.node) + ']');
+	}
+
+	// Verify syntax support
+	// Convert syntaxes into a sorted lowercase string
+	var syntaxes = edition.syntaxes && edition.syntaxes.map(function (i) {
+		return i.toLowerCase();
+	}).sort() || [];
+	for (var index = 0; index < syntaxes.length; index++) {
+		var _syntax = syntaxes[index];
+		var blacklisted = blacklist[_syntax];
+		if (blacklisted) {
+			throw new Errlop('Skipping edition [' + edition.description + '] because it contained a blacklisted syntax [' + _syntax + ']', blacklisted);
+		}
+	}
+
+	// Load the edition
+	var entry = pathUtil.resolve(opts.cwd || '', edition.directory, opts.entry || edition.entry);
+	try {
+		return opts.require(entry);
+	} catch (loadError) {
+		// Note the error with more details
+		throw new Errlop('Skipped edition [' + edition.description + '] at entry [' + entry + '] because it failed to load', loadError);
+	}
+}
+
+/**
+ * Cycles through a list of editions, returning the first suitable edition that it was able to load
+ * @param {Array<Edition>} editions
+ * @param {Options} opts
+ * @returns {*} the result of the loaded edition
+ * @throws {Error} an error if a suitable edition was unable to be resolved
+ * @public
+ */
+function requireEditions(editions, opts) {
+	// Check
+	if (!editions || editions.length === 0) {
+		if (opts.packagePath) {
+			throw new Errlop('There were no editions specified for package [' + opts.packagePath + ']');
+		} else {
+			throw new Errlop('There were no editions specified');
+		}
+	}
+
+	// Note the last error message
+	var result = void 0,
+	    editionsError = null,
+	    loaded = false;
+
+	// Cycle through the editions
+	for (var _i = 0; _i < editions.length; ++_i) {
+		var edition = editions[_i];
+		try {
+			result = requireEdition(edition, opts);
+			loaded = true;
+			break;
+		} catch (editionError) {
+			if (editionError.level === 'fatal') {
+				editionsError = editionError;
+				break;
+			} else if (editionsError) {
+				editionsError = new Errlop(editionsError, editionError);
+			} else {
+				editionsError = editionError;
+			}
+		}
+	}
+
+	if (loaded) {
+		var verbose = opts.verbose == null ? VERBOSE : opts.verbose;
+		if (editionsError && verbose) {
+			var stderr = opts.stderr || process.stderr;
+			stderr.write(editionsError.stack + '\n');
+		}
+		return result;
+	} else if (editionsError) {
+		if (opts.packagePath) {
+			throw new Errlop('There were no suitable editions for package [' + opts.packagePath + ']', editionsError);
+		} else {
+			throw new Errlop('There were no suitable editions', editionsError);
+		}
+	}
+}
+
+/**
+ * Cycle through the editions for a package and require the correct one
+ * @param {Options.cwd} cwd
+ * @param {Options.require} require
+ * @param {Options.entry} [entry]
+ * @returns {*} the result of the loaded edition
+ * @throws {Error} an error if a suitable edition was unable to be resolved
+ * @public
+ */
+function requirePackage(cwd, require, entry) {
+	// Load the package.json file to fetch `name` for debugging and `editions` for loading
+	var packagePath = pathUtil.resolve(cwd, 'package.json');
+
+	var _require = require(packagePath),
+	    editions = _require.editions;
+
+	var opts = { packagePath: packagePath, cwd: cwd, require: require, entry: entry };
+	return requireEditions(editions, opts);
+}
+
+// Exports
+module.exports = { requireEdition: requireEdition, requireEditions: requireEditions, requirePackage: requirePackage };

+ 211 - 0
src/jfw/modules/pc/client/apple/node_modules/editions/package.json

@@ -0,0 +1,211 @@
+{
+  "_from": "editions@^2.0.2",
+  "_id": "editions@2.0.2",
+  "_inBundle": false,
+  "_integrity": "sha512-0B8aSTWUu9+JW99zHoeogavCi+lkE5l35FK0OKe0pCobixJYoeof3ZujtqYzSsU2MskhRadY5V9oWUuyG4aJ3A==",
+  "_location": "/editions",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "editions@^2.0.2",
+    "name": "editions",
+    "escapedName": "editions",
+    "rawSpec": "^2.0.2",
+    "saveSpec": null,
+    "fetchSpec": "^2.0.2"
+  },
+  "_requiredBy": [
+    "/getmac",
+    "/typechecker"
+  ],
+  "_resolved": "https://registry.npmjs.org/editions/-/editions-2.0.2.tgz",
+  "_shasum": "54fdac6fb24b0a1a72ffc1ba0126c10602c3e0bd",
+  "_spec": "editions@^2.0.2",
+  "_where": "E:\\nwjs-sdk-v0.34.2-win-x64\\node_modules\\getmac",
+  "author": {
+    "name": "2016+ Bevry Pty Ltd",
+    "email": "us@bevry.me",
+    "url": "http://bevry.me"
+  },
+  "babel": {
+    "env": {
+      "edition-browsers": {
+        "presets": [
+          [
+            "env",
+            {
+              "targets": {
+                "browsers": "defaults"
+              }
+            }
+          ]
+        ]
+      },
+      "edition-node-0.8": {
+        "presets": [
+          [
+            "env",
+            {
+              "targets": {
+                "node": "0.8"
+              }
+            }
+          ]
+        ]
+      }
+    }
+  },
+  "badges": {
+    "list": [
+      "travisci",
+      "npmversion",
+      "npmdownloads",
+      "daviddm",
+      "daviddmdev",
+      "---",
+      "patreon",
+      "opencollective",
+      "flattr",
+      "paypal",
+      "bitcoin",
+      "wishlist"
+    ],
+    "config": {
+      "patreonUsername": "bevry",
+      "opencollectiveUsername": "bevry",
+      "flattrUsername": "balupton",
+      "paypalURL": "https://bevry.me/paypal",
+      "bitcoinURL": "https://bevry.me/bitcoin",
+      "wishlistURL": "https://bevry.me/wishlist"
+    }
+  },
+  "browser": "edition-browsers/index.js",
+  "bugs": {
+    "url": "https://github.com/bevry/editions/issues"
+  },
+  "bundleDependencies": false,
+  "contributors": [
+    {
+      "name": "Benjamin Lupton",
+      "email": "b@lupton.cc",
+      "url": "http://balupton.com"
+    },
+    {
+      "name": "Zlatan Vasović",
+      "email": "zlatanvasovic@gmail.com",
+      "url": "http://zdroid.github.io"
+    }
+  ],
+  "dependencies": {
+    "errlop": "^1.0.2",
+    "semver": "^5.5.0"
+  },
+  "deprecated": false,
+  "description": "Publish multiple editions for your JavaScript packages consistently and easily (e.g. source edition, esnext edition, es2015 edition)",
+  "devDependencies": {
+    "assert-helpers": "^4.5.1",
+    "babel-cli": "^6.26.0",
+    "babel-preset-env": "^1.7.0",
+    "documentation": "^8.1.2",
+    "eslint": "^5.5.0",
+    "joe": "^2.0.2",
+    "joe-reporter-console": "^2.0.2",
+    "projectz": "^1.4.0",
+    "surge": "^0.20.1"
+  },
+  "editions": [
+    {
+      "description": "esnext source code with require for modules",
+      "directory": "source",
+      "entry": "index.js",
+      "syntaxes": [
+        "javascript",
+        "esnext",
+        "require"
+      ],
+      "engines": {
+        "node": ">=6",
+        "browsers": false
+      }
+    },
+    {
+      "description": "esnext compiled for browsers with require for modules",
+      "directory": "edition-browsers",
+      "entry": "index.js",
+      "syntaxes": [
+        "javascript",
+        "require"
+      ],
+      "engines": {
+        "node": false,
+        "browsers": "defaults"
+      }
+    },
+    {
+      "description": "esnext compiled for node.js >=0.8 with require for modules",
+      "directory": "edition-node-0.8",
+      "entry": "index.js",
+      "syntaxes": [
+        "javascript",
+        "require"
+      ],
+      "engines": {
+        "node": "0.8 || 0.10 || 0.12 || 4 || 6 || 8 || 10",
+        "browsers": false
+      }
+    }
+  ],
+  "engines": {
+    "node": ">=0.8"
+  },
+  "homepage": "https://github.com/bevry/editions",
+  "keywords": [
+    "editions",
+    "edition",
+    "versions",
+    "syntaxes",
+    "esnext",
+    "jsnext",
+    "es2015",
+    "es6",
+    "es6+"
+  ],
+  "license": "MIT",
+  "main": "edition-node-0.8/index.js",
+  "maintainers": [
+    {
+      "name": "Benjamin Lupton",
+      "email": "b@lupton.cc",
+      "url": "http://balupton.com"
+    }
+  ],
+  "name": "editions",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/bevry/editions.git"
+  },
+  "scripts": {
+    "our:clean": "rm -Rf ./docs ./edition:* ./es2015 ./es5 ./out",
+    "our:compile": "npm run our:compile:edition-browsers && npm run our:compile:edition-node-0.8",
+    "our:compile:edition-browsers": "env BABEL_ENV=edition-browsers babel --out-dir ./edition-browsers ./source",
+    "our:compile:edition-node-0.8": "env BABEL_ENV=edition-node-0.8 babel --out-dir ./edition-node-0.8 ./source",
+    "our:deploy": "echo no need for this project",
+    "our:meta": "npm run our:meta:docs && npm run our:meta:projectz",
+    "our:meta:docs": "documentation build -f html -o ./docs -g --shallow ./source/**.js",
+    "our:meta:projectz": "projectz compile",
+    "our:release": "npm run our:release:prepare && npm run our:release:check-changelog && npm run our:release:check-dirty && npm run our:release:tag && npm run our:release:push",
+    "our:release:check-changelog": "cat ./HISTORY.md | grep v$npm_package_version || (echo add a changelog entry for v$npm_package_version && exit -1)",
+    "our:release:check-dirty": "git diff --exit-code",
+    "our:release:prepare": "npm run our:clean && npm run our:compile && npm run our:test && npm run our:meta",
+    "our:release:push": "git push origin master && git push origin --tags",
+    "our:release:tag": "export MESSAGE=$(cat ./HISTORY.md | sed -n \"/## v$npm_package_version/,/##/p\" | sed 's/## //' | awk 'NR>1{print buf}{buf = $0}') && test \"$MESSAGE\" || (echo 'proper changelog entry not found' && exit -1) && git tag v$npm_package_version -am \"$MESSAGE\"",
+    "our:setup": "npm run our:setup:npm",
+    "our:setup:npm": "npm install",
+    "our:test": "npm run our:verify && npm test",
+    "our:verify": "npm run our:verify:eslint",
+    "our:verify:eslint": "eslint --fix ./source",
+    "test": "node --harmony ./edition-node-0.8/test.js --joe-reporter=console"
+  },
+  "version": "2.0.2"
+}

+ 187 - 0
src/jfw/modules/pc/client/apple/node_modules/editions/source/index.js

@@ -0,0 +1,187 @@
+/* eslint no-console:0 */
+'use strict'
+
+/**
+ * @typedef {Object} Edition
+ * @property {string} description
+ * @property {string} directory
+ * @property {string} entry
+ * @property {Array<string>} syntaxes
+ * @property {false|Object.<string, string|boolean>} engines
+ * @public
+ */
+
+/**
+ * @typedef {Object} Options
+ * @property {Function} require - the require method of the calling module, used to ensure require paths remain correct
+ * @property {string} [packagePath] - if provided, this is used for debugging
+ * @property {boolean} [verbose] - if provided, any error loading an edition will be logged. By default, errors are only logged if all editions failed. If not provided, process.env.EDITIONS_VERBOSE is used.
+ * @property {string} [cwd] - if provided, this will be the cwd for entries
+ * @property {string} [entry] - if provided, should be a relative path to the entry point of the edition
+ * @property {string} [package] - if provided, should be the name of the package that we are loading the editions for
+ * @property {stream.Writable} [stderr] - if not provided, will use process.stderr instead. It is the stream that verbose errors are logged to.
+ * @public
+ */
+
+// Imports
+const pathUtil = require('path')
+const semver = require('semver')
+const Errlop = require('errlop')
+
+// Helpers
+function stringify (value) {
+	return typeof value === 'string' ? value : JSON.stringify(value)
+}
+
+// Environment fetching
+const BLACKLIST = process.env.EDITIONS_SYNTAX_BLACKLIST && process.env.EDITIONS_SYNTAX_BLACKLIST.split(/[, ]+/g)
+const NODE_VERSION = process.versions.node
+const VERBOSE = process.env.EDITIONS_VERBOSE === true || process.env.EDITIONS_VERBOSE === 'yes' || process.env.EDITIONS_VERBOSE === 'true' || false
+
+// Cache of which syntax combinations are supported or unsupported
+// Object.<string, Error>
+const blacklist = {}
+
+// Check the environment configuration for a syntax blacklist
+if (BLACKLIST) {
+	for (let i = 0; i < BLACKLIST.length; ++i) {
+		const syntax = BLACKLIST[i].trim().toLowerCase()
+		blacklist[syntax] = new Errlop(`The EDITIONS_SYNTAX_BLACKLIST environment variable blacklisted the syntax [${syntax}]`)
+	}
+}
+
+// Blacklist the syntax 'esnext' if our node version is below 0.12
+if (semver.satisfies(NODE_VERSION, '<0.12')) {
+	blacklist.esnext = new Error('The esnext syntax is skipped on early node versions as attempting to use esnext features will output debugging information on these node versions')
+}
+
+/**
+ * Attempt to load a specific edition
+ * @param {Edition} edition
+ * @param {Options} opts
+ * @returns {*} the result of the loaded edition
+ * @throws {Error} an error if the edition failed to load
+ * @public
+ */
+function requireEdition (edition, opts) {
+	// Verify the edition is valid
+	if (!edition.description || !edition.directory || !edition.entry || edition.engines == null) {
+		const editionInvalidError = new Errlop(`Each edition must have its [description, directory, entry, engines] fields defined, yet all it had was [${Object.keys(edition).join(', ')}]`)
+		editionInvalidError.level = 'fatal'
+		throw editionInvalidError
+	}
+
+	// Verify engine support
+	if (edition.engines === false) {
+		throw new Errlop(`Skipping edition [${edition.description}] because its engines field was false`)
+	}
+	if (!edition.engines.node) {
+		throw new Errlop(`Skipping edition [${edition.description}] because its .engines.node field was falsey`)
+	}
+	if (edition.engines.node !== true && semver.satisfies(NODE_VERSION, edition.engines.node) === false) {
+		throw new Errlop(`Skipping edition [${edition.description}] because our current node version [${NODE_VERSION}] is not supported by its [${stringify(edition.engines.node)}]`)
+	}
+
+	// Verify syntax support
+	// Convert syntaxes into a sorted lowercase string
+	const syntaxes = (edition.syntaxes && edition.syntaxes.map((i) => i.toLowerCase()).sort()) || []
+	for (let index = 0; index < syntaxes.length; index++) {
+		const syntax = syntaxes[index]
+		const blacklisted = blacklist[syntax]
+		if (blacklisted) {
+			throw new Errlop(`Skipping edition [${edition.description}] because it contained a blacklisted syntax [${syntax}]`, blacklisted)
+		}
+	}
+
+	// Load the edition
+	const entry = pathUtil.resolve(opts.cwd || '', edition.directory, opts.entry || edition.entry)
+	try {
+		return opts.require(entry)
+	}
+	catch (loadError) {
+		// Note the error with more details
+		throw new Errlop(`Skipped edition [${edition.description}] at entry [${entry}] because it failed to load`, loadError)
+	}
+}
+
+/**
+ * Cycles through a list of editions, returning the first suitable edition that it was able to load
+ * @param {Array<Edition>} editions
+ * @param {Options} opts
+ * @returns {*} the result of the loaded edition
+ * @throws {Error} an error if a suitable edition was unable to be resolved
+ * @public
+ */
+function requireEditions (editions, opts) {
+	// Check
+	if (!editions || editions.length === 0) {
+		if (opts.packagePath) {
+			throw new Errlop(`There were no editions specified for package [${opts.packagePath}]`)
+		}
+		else {
+			throw new Errlop('There were no editions specified')
+		}
+	}
+
+	// Note the last error message
+	let result, editionsError = null, loaded = false
+
+	// Cycle through the editions
+	for (let i = 0; i < editions.length; ++i) {
+		const edition = editions[i]
+		try {
+			result = requireEdition(edition, opts)
+			loaded = true
+			break
+		}
+		catch (editionError) {
+			if (editionError.level === 'fatal') {
+				editionsError = editionError
+				break
+			}
+			else if (editionsError) {
+				editionsError = new Errlop(editionsError, editionError)
+			}
+			else {
+				editionsError = editionError
+			}
+		}
+	}
+
+	if (loaded) {
+		const verbose = opts.verbose == null ? VERBOSE : opts.verbose
+		if (editionsError && verbose) {
+			const stderr = opts.stderr || process.stderr
+			stderr.write(editionsError.stack + '\n')
+		}
+		return result
+	}
+	else if (editionsError) {
+		if (opts.packagePath) {
+			throw new Errlop(`There were no suitable editions for package [${opts.packagePath}]`, editionsError)
+		}
+		else {
+			throw new Errlop('There were no suitable editions', editionsError)
+		}
+	}
+}
+
+/**
+ * Cycle through the editions for a package and require the correct one
+ * @param {Options.cwd} cwd
+ * @param {Options.require} require
+ * @param {Options.entry} [entry]
+ * @returns {*} the result of the loaded edition
+ * @throws {Error} an error if a suitable edition was unable to be resolved
+ * @public
+ */
+function requirePackage (cwd, require, entry) {
+	// Load the package.json file to fetch `name` for debugging and `editions` for loading
+	const packagePath = pathUtil.resolve(cwd, 'package.json')
+	const { editions } = require(packagePath)
+	const opts = { packagePath, cwd, require, entry }
+	return requireEditions(editions, opts)
+}
+
+// Exports
+module.exports = { requireEdition, requireEditions, requirePackage }

+ 15 - 0
src/jfw/modules/pc/client/apple/node_modules/errlop/HISTORY.md

@@ -0,0 +1,15 @@
+# History
+
+## v1.0.3 2018 August 19
+- Add duck typing for node version 4 and under
+- Prevent additional crashes on node version 4
+
+## v1.0.2 2018 July 27
+- More effecient node version 4 crash prevention
+
+## v1.0.1 2018 July 27
+- Support node version 0.8
+- Prevent a peculiar  crash on node version 4
+
+## v1.0.0 2018 July 21
+- Initial working release

+ 23 - 0
src/jfw/modules/pc/client/apple/node_modules/errlop/LICENSE.md

@@ -0,0 +1,23 @@
+<!-- LICENSEFILE/ -->
+
+<h1>License</h1>
+
+Unless stated otherwise all works are:
+
+<ul><li>Copyright &copy; 2018+ Benjamin Lupton</li></ul>
+
+and licensed under:
+
+<ul><li><a href="http://spdx.org/licenses/MIT.html">MIT License</a></li></ul>
+
+<h2>MIT License</h2>
+
+<pre>
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+</pre>
+
+<!-- /LICENSEFILE -->

+ 182 - 0
src/jfw/modules/pc/client/apple/node_modules/errlop/README.md

@@ -0,0 +1,182 @@
+<!-- TITLE/ -->
+
+<h1>errlop</h1>
+
+<!-- /TITLE -->
+
+
+<!-- BADGES/ -->
+
+<span class="badge-travisci"><a href="http://travis-ci.org/bevry/errlop" title="Check this project's build status on TravisCI"><img src="https://img.shields.io/travis/bevry/errlop/master.svg" alt="Travis CI Build Status" /></a></span>
+<span class="badge-npmversion"><a href="https://npmjs.org/package/errlop" title="View this project on NPM"><img src="https://img.shields.io/npm/v/errlop.svg" alt="NPM version" /></a></span>
+<span class="badge-npmdownloads"><a href="https://npmjs.org/package/errlop" title="View this project on NPM"><img src="https://img.shields.io/npm/dm/errlop.svg" alt="NPM downloads" /></a></span>
+<span class="badge-daviddm"><a href="https://david-dm.org/bevry/errlop" title="View the status of this project's dependencies on DavidDM"><img src="https://img.shields.io/david/bevry/errlop.svg" alt="Dependency Status" /></a></span>
+<span class="badge-daviddmdev"><a href="https://david-dm.org/bevry/errlop#info=devDependencies" title="View the status of this project's development dependencies on DavidDM"><img src="https://img.shields.io/david/dev/bevry/errlop.svg" alt="Dev Dependency Status" /></a></span>
+<br class="badge-separator" />
+<span class="badge-patreon"><a href="https://patreon.com/bevry" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
+<span class="badge-opencollective"><a href="https://opencollective.com/bevry" title="Donate to this project using Open Collective"><img src="https://img.shields.io/badge/open%20collective-donate-yellow.svg" alt="Open Collective donate button" /></a></span>
+<span class="badge-flattr"><a href="https://flattr.com/profile/balupton" title="Donate to this project using Flattr"><img src="https://img.shields.io/badge/flattr-donate-yellow.svg" alt="Flattr donate button" /></a></span>
+<span class="badge-paypal"><a href="https://bevry.me/paypal" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" alt="PayPal donate button" /></a></span>
+<span class="badge-bitcoin"><a href="https://bevry.me/bitcoin" title="Donate once-off to this project using Bitcoin"><img src="https://img.shields.io/badge/bitcoin-donate-yellow.svg" alt="Bitcoin donate button" /></a></span>
+<span class="badge-wishlist"><a href="https://bevry.me/wishlist" title="Buy an item on our wishlist for us"><img src="https://img.shields.io/badge/wishlist-donate-yellow.svg" alt="Wishlist browse button" /></a></span>
+
+<!-- /BADGES -->
+
+
+<!-- DESCRIPTION/ -->
+
+An extended Error class that envelops a parent error, such that the stack trace contains the causation
+
+<!-- /DESCRIPTION -->
+
+
+<!-- INSTALL/ -->
+
+<h2>Install</h2>
+
+<a href="https://npmjs.com" title="npm is a package manager for javascript"><h3>NPM</h3></a><ul>
+<li>Install: <code>npm install --save errlop</code></li>
+<li>Module: <code>require('errlop')</code></li></ul>
+
+<a href="http://browserify.org" title="Browserify lets you require('modules') in the browser by bundling up all of your dependencies"><h3>Browserify</h3></a><ul>
+<li>Install: <code>npm install --save errlop</code></li>
+<li>Module: <code>require('errlop')</code></li>
+<li>CDN URL: <code>//wzrd.in/bundle/errlop@1.0.3</code></li></ul>
+
+<a href="http://enderjs.com" title="Ender is a full featured package manager for your browser"><h3>Ender</h3></a><ul>
+<li>Install: <code>ender add errlop</code></li>
+<li>Module: <code>require('errlop')</code></li></ul>
+
+<h3><a href="https://github.com/bevry/editions" title="Editions are the best way to produce and consume packages you care about.">Editions</a></h3>
+
+<p>This package is published with the following editions:</p>
+
+<ul><li><code>errlop</code> aliases <code>errlop/index.js</code> which uses <a href="https://github.com/bevry/editions" title="Editions are the best way to produce and consume packages you care about.">Editions</a> to automatically select the correct edition for the consumers environment</li>
+<li><code>errlop/source/index.js</code> is Source + <a href="https://babeljs.io/docs/learn-es2015/" title="ECMAScript Next">ESNext</a> + <a href="https://nodejs.org/dist/latest-v5.x/docs/api/modules.html" title="Node/CJS Modules">Require</a></li>
+<li><code>errlop/es2015/index.js</code> is <a href="https://babeljs.io" title="The compiler for writing next generation JavaScript">Babel</a> Compiled + <a href="http://babeljs.io/docs/plugins/preset-es2015/" title="ECMAScript 2015">ES2015</a> + <a href="https://nodejs.org/dist/latest-v5.x/docs/api/modules.html" title="Node/CJS Modules">Require</a></li></ul>
+
+<p>Older environments may need <a href="https://babeljs.io/docs/usage/polyfill/" title="A polyfill that emulates missing ECMAScript environment features">Babel's Polyfill</a> or something similar.</p>
+
+<!-- /INSTALL -->
+
+
+## Usage
+
+[Complete API Documentation.](http://master.errlop.bevry.surge.sh/docs/)
+
+``` javascript
+const Errlop = require('errlop')
+const a = new Errlop('AError')
+const b = new Errlop('BError', a)
+const c = Errlop.create('CError', b)
+console.log(c.stack)
+/*
+Error: CError
+    at Function.create (/Users/balupton/Projects/active/errlop/source/index.js:92:10)
+    at Object.<anonymous> (/Users/balupton/Projects/active/errlop/example.js:6:18)
+    at Module._compile (internal/modules/cjs/loader.js:689:30)
+    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
+    at Module.load (internal/modules/cjs/loader.js:599:32)
+    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
+    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
+    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
+    at startup (internal/bootstrap/node.js:266:19)
+    at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)
+↳ Error: BError
+    at Object.<anonymous> (/Users/balupton/Projects/active/errlop/example.js:5:11)
+    at Module._compile (internal/modules/cjs/loader.js:689:30)
+    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
+    at Module.load (internal/modules/cjs/loader.js:599:32)
+    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
+    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
+    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
+    at startup (internal/bootstrap/node.js:266:19)
+    at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)
+↳ Error: AError
+    at Object.<anonymous> (/Users/balupton/Projects/active/errlop/example.js:4:11)
+    at Module._compile (internal/modules/cjs/loader.js:689:30)
+    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
+    at Module.load (internal/modules/cjs/loader.js:599:32)
+    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
+    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
+    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
+    at startup (internal/bootstrap/node.js:266:19)
+*/
+console.log(c.orphanStack)
+/*
+Error: CError
+    at Function.create (/Users/balupton/Projects/active/errlop/source/index.js:92:10)
+    at Object.<anonymous> (/Users/balupton/Projects/active/errlop/example.js:6:18)
+    at Module._compile (internal/modules/cjs/loader.js:689:30)
+    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
+    at Module.load (internal/modules/cjs/loader.js:599:32)
+    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
+    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
+    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
+    at startup (internal/bootstrap/node.js:266:19)
+    at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)
+*/
+```
+
+
+<!-- HISTORY/ -->
+
+<h2>History</h2>
+
+<a href="https://github.com/bevry/errlop/blob/master/HISTORY.md#files">Discover the release history by heading on over to the <code>HISTORY.md</code> file.</a>
+
+<!-- /HISTORY -->
+
+
+<!-- CONTRIBUTE/ -->
+
+<h2>Contribute</h2>
+
+<a href="https://github.com/bevry/errlop/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a>
+
+<!-- /CONTRIBUTE -->
+
+
+<!-- BACKERS/ -->
+
+<h2>Backers</h2>
+
+<h3>Maintainers</h3>
+
+No maintainers yet! Will you be the first?
+
+<h3>Sponsors</h3>
+
+No sponsors yet! Will you be the first?
+
+<span class="badge-patreon"><a href="https://patreon.com/bevry" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
+<span class="badge-opencollective"><a href="https://opencollective.com/bevry" title="Donate to this project using Open Collective"><img src="https://img.shields.io/badge/open%20collective-donate-yellow.svg" alt="Open Collective donate button" /></a></span>
+<span class="badge-flattr"><a href="https://flattr.com/profile/balupton" title="Donate to this project using Flattr"><img src="https://img.shields.io/badge/flattr-donate-yellow.svg" alt="Flattr donate button" /></a></span>
+<span class="badge-paypal"><a href="https://bevry.me/paypal" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" alt="PayPal donate button" /></a></span>
+<span class="badge-bitcoin"><a href="https://bevry.me/bitcoin" title="Donate once-off to this project using Bitcoin"><img src="https://img.shields.io/badge/bitcoin-donate-yellow.svg" alt="Bitcoin donate button" /></a></span>
+<span class="badge-wishlist"><a href="https://bevry.me/wishlist" title="Buy an item on our wishlist for us"><img src="https://img.shields.io/badge/wishlist-donate-yellow.svg" alt="Wishlist browse button" /></a></span>
+
+<h3>Contributors</h3>
+
+These amazing people have contributed code to this project:
+
+<ul><li><a href="http://balupton.com">Benjamin Lupton</a> — <a href="https://github.com/bevry/errlop/commits?author=balupton" title="View the GitHub contributions of Benjamin Lupton on repository bevry/errlop">view contributions</a></li></ul>
+
+<a href="https://github.com/bevry/errlop/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a>
+
+<!-- /BACKERS -->
+
+
+<!-- LICENSE/ -->
+
+<h2>License</h2>
+
+Unless stated otherwise all works are:
+
+<ul><li>Copyright &copy; 2018+ Benjamin Lupton</li></ul>
+
+and licensed under:
+
+<ul><li><a href="http://spdx.org/licenses/MIT.html">MIT License</a></li></ul>
+
+<!-- /LICENSE -->

+ 184 - 0
src/jfw/modules/pc/client/apple/node_modules/errlop/es2015/index.js

@@ -0,0 +1,184 @@
+'use strict';
+
+/**
+ * Only accept codes that are numbers, otherwise discard them
+ * @param {*} code
+ * @returns {number}
+ * @private
+ */
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+function parseCode(code) {
+	var number = Number(code);
+	if (isNaN(number)) return null;
+	return number;
+}
+
+/**
+ * Fetch the code from the value
+ * @param {Object|Error} value
+ * @returns {boolean}
+ * @private
+ */
+function fetchCode(value) {
+	return value && (parseCode(value.exitCode) || parseCode(value.errno) || parseCode(value.code));
+}
+
+/**
+ * Prevent [a weird error on node version 4](https://github.com/bevry/errlop/issues/1) and below.
+ * @param {*} value
+ * @returns {boolean}
+ * @private
+ */
+function isValid(value) {
+	/* eslint no-use-before-define:0 */
+	return value instanceof Error || Errlop.isErrlop(value);
+}
+
+/**
+ * Create an instance of an error, using a message, as well as an optional parent.
+ * If the parent is provided, then the `fullStack` property will include its stack too
+ * @class Errlop
+ * @constructor
+ * @param {Errlop|Error|Object|string} input
+ * @param {Errlop|Error} [parent]
+ * @public
+ */
+
+var Errlop = function (_Error) {
+	_inherits(Errlop, _Error);
+
+	function Errlop(input, parent) {
+		_classCallCheck(this, Errlop);
+
+		if (!input) throw new Error('Attempted to create an Errlop without a input');
+
+		// Instantiate with the above
+
+		/**
+   * Duck typing as node 4 and intanceof does not work for error extensions
+   * @type {Errlop}
+   * @public
+   */
+		var _this = _possibleConstructorReturn(this, (Errlop.__proto__ || Object.getPrototypeOf(Errlop)).call(this, input.message || input));
+
+		_this.klass = Errlop;
+
+		/**
+   * The parent error if it was provided.
+   * If a parent was provided, then use that, otherwise use the input's parent, if it exists.
+   * @type {Error?}
+   * @public
+   */
+		_this.parent = parent || input.parent;
+
+		/**
+   * An array of all the ancestors. From parent, to grand parent, and so on.
+   * @type {Array<Error>}
+   * @public
+   */
+		_this.ancestors = [];
+		var ancestor = _this.parent;
+		while (ancestor) {
+			_this.ancestors.push(ancestor);
+			ancestor = ancestor.parent;
+		}
+
+		// this code must support node 0.8, as well as prevent a weird bug in node v4: https://travis-ci.org/bevry/editions/jobs/408828147
+		var exitCode = fetchCode(input);
+		if (exitCode == null) exitCode = fetchCode(_this);
+		for (var index = 0; index < _this.ancestors.length && exitCode == null; ++index) {
+			var error = _this.ancestors[index];
+			if (isValid(error)) exitCode = fetchCode(error);
+		}
+
+		/**
+   * A numeric code to use for the exit status if desired by the consumer.
+   * It cycles through [input, this, ...ancestors] until it finds the first [exitCode, errno, code] that is valid.
+   * @type {Number?}
+   * @public
+   */
+		_this.exitCode = exitCode;
+
+		/**
+   * The stack for our instance alone, without any parents.
+   * If the input contained a stack, then use that.
+   * @type {string}
+   * @public
+   */
+		_this.orphanStack = (input.stack || _this.stack).toString();
+
+		/**
+   * The stack which now contains the accumalated stacks of its ancestors.
+   * This is used instead of an alias like `fullStack` or the like, to ensure existing code that uses `err.stack` doesn't need to be changed to remain functional.
+   * @type {string}
+   * @public
+   */
+		_this.stack = [_this.orphanStack].concat(_toConsumableArray(_this.ancestors)).reduce(function (accumulator, error) {
+			return accumulator + '\n\u21B3 ' + (error.orphanStack || error.stack || error);
+		});
+		return _this;
+	}
+
+	/**
+  * Syntatic sugar for Errlop class creation.
+  * Enables `Errlop.create(...args)` to achieve `new Errlop(...args)`
+  * @param {...*} args
+  * @returns {Errlop}
+  * @static
+  * @public
+  */
+
+
+	_createClass(Errlop, null, [{
+		key: 'create',
+		value: function create() {
+			for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+				args[_key] = arguments[_key];
+			}
+
+			return new (Function.prototype.bind.apply(this, [null].concat(args)))();
+		}
+
+		/**
+   * Check whether or not the value is an Errlop instance
+   * @param {*} value
+   * @returns {boolean}
+   * @static
+   * @public
+   */
+
+	}, {
+		key: 'isErrlop',
+		value: function isErrlop(value) {
+			return value && (value instanceof this || value.klass === this);
+		}
+
+		/**
+   * Ensure that the value is an Errlop instance
+   * @param {*} value
+   * @returns {Errlop}
+   * @static
+   * @public
+   */
+
+	}, {
+		key: 'ensure',
+		value: function ensure(value) {
+			return this.isErrlop(value) ? value : this.create(value);
+		}
+	}]);
+
+	return Errlop;
+}(Error);
+
+module.exports = Errlop;

+ 3 - 0
src/jfw/modules/pc/client/apple/node_modules/errlop/index.js

@@ -0,0 +1,3 @@
+'use strict'
+
+module.exports = require('editions').requirePackage(__dirname, require)

+ 16 - 0
src/jfw/modules/pc/client/apple/node_modules/errlop/node_modules/editions/.flowconfig

@@ -0,0 +1,16 @@
+# 2018 January 24
+# https://github.com/bevry/base
+[ignore]
+.*/docs/.*
+.*/es2015/.*
+.*/test/.*
+.*/node_modules/documentation/.*
+.*/node_modules/projectz/.*
+
+[options]
+module.ignore_non_literal_requires=true
+esproposal.class_static_fields=enable
+esproposal.class_instance_fields=enable
+
+[version]
+>=0.23 <1

+ 44 - 0
src/jfw/modules/pc/client/apple/node_modules/errlop/node_modules/editions/HISTORY.md

@@ -0,0 +1,44 @@
+# History
+
+## v1.3.4 2018 January 31
+- Updated base files
+
+## v1.3.3 2016 November 4
+- Properly add node 0.8 support
+
+## v1.3.2 2016 November 4
+- Added node 0.8 support
+
+## v1.3.1 2016 October 11
+- Fixed failure to load editions that had the edition directory within the edition entry
+  - Thanks to [Jordan Harband](https://github.com/ljharb) for [issue #20](https://github.com/bevry/editions/issues/20)
+
+## v1.3.0 2016 October 11
+- Added support for `EDITIONS_SYNTAX_BLACKLIST` environment variable
+  - Thanks to [Damon Maria](https://github.com/damonmaria) for [issue #10](https://github.com/bevry/editions/issues/10)
+- Dropped need for `DEBUG_BEVRY_EDITIONS` as failures will not output all the necessary debugging information
+
+## v1.2.1 2016 October 10
+- Change `esnext` skip from v8 engines < 4 to node engines < 0.12
+
+## v1.2.0 2016 October 10
+- Skip syntaxes that require preprocessors
+- Skip `import` syntax, as the `module` field inside `package.json` skips the autoloader if supported
+- Skip `esnext` syntax on v8 engines < 4
+
+## v1.1.2 2016 June 16
+- Parent errors are now displayed in a more sensible way
+
+## v1.1.1 2016 March 20
+- Errors and debug messages are now more useful
+  - Closes https://github.com/bevry/editions/issues/5
+
+## v1.1.0 2016 March 20
+- Added support for custom entry point overrides
+- Debugging goes to `console.error` (stderr) rather than `console.log` (stdout)
+  - Closes https://github.com/bevry/editions/issues/2
+- Added tests
+  - Closes https://github.com/bevry/editions/issues/4
+
+## v1.0.1 2016 March 9
+- Initial release

+ 23 - 0
src/jfw/modules/pc/client/apple/node_modules/errlop/node_modules/editions/LICENSE.md

@@ -0,0 +1,23 @@
+<!-- LICENSEFILE/ -->
+
+<h1>License</h1>
+
+Unless stated otherwise all works are:
+
+<ul><li>Copyright &copy; 2016+ <a href="http://bevry.me">Bevry Pty Ltd</a></li></ul>
+
+and licensed under:
+
+<ul><li><a href="http://spdx.org/licenses/MIT.html">MIT License</a></li></ul>
+
+<h2>MIT License</h2>
+
+<pre>
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+</pre>
+
+<!-- /LICENSEFILE -->

+ 109 - 0
src/jfw/modules/pc/client/apple/node_modules/errlop/node_modules/editions/README.md

@@ -0,0 +1,109 @@
+<!-- TITLE/ -->
+
+<h1>editions</h1>
+
+<!-- /TITLE -->
+
+
+<!-- BADGES/ -->
+
+<span class="badge-travisci"><a href="http://travis-ci.org/bevry/editions" title="Check this project's build status on TravisCI"><img src="https://img.shields.io/travis/bevry/editions/master.svg" alt="Travis CI Build Status" /></a></span>
+<span class="badge-npmversion"><a href="https://npmjs.org/package/editions" title="View this project on NPM"><img src="https://img.shields.io/npm/v/editions.svg" alt="NPM version" /></a></span>
+<span class="badge-npmdownloads"><a href="https://npmjs.org/package/editions" title="View this project on NPM"><img src="https://img.shields.io/npm/dm/editions.svg" alt="NPM downloads" /></a></span>
+<span class="badge-daviddm"><a href="https://david-dm.org/bevry/editions" title="View the status of this project's dependencies on DavidDM"><img src="https://img.shields.io/david/bevry/editions.svg" alt="Dependency Status" /></a></span>
+<span class="badge-daviddmdev"><a href="https://david-dm.org/bevry/editions#info=devDependencies" title="View the status of this project's development dependencies on DavidDM"><img src="https://img.shields.io/david/dev/bevry/editions.svg" alt="Dev Dependency Status" /></a></span>
+<br class="badge-separator" />
+<span class="badge-patreon"><a href="https://patreon.com/bevry" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
+<span class="badge-opencollective"><a href="https://opencollective.com/bevry" title="Donate to this project using Open Collective"><img src="https://img.shields.io/badge/open%20collective-donate-yellow.svg" alt="Open Collective donate button" /></a></span>
+<span class="badge-gratipay"><a href="https://www.gratipay.com/bevry" title="Donate weekly to this project using Gratipay"><img src="https://img.shields.io/badge/gratipay-donate-yellow.svg" alt="Gratipay donate button" /></a></span>
+<span class="badge-flattr"><a href="https://flattr.com/profile/balupton" title="Donate to this project using Flattr"><img src="https://img.shields.io/badge/flattr-donate-yellow.svg" alt="Flattr donate button" /></a></span>
+<span class="badge-paypal"><a href="https://bevry.me/paypal" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" alt="PayPal donate button" /></a></span>
+<span class="badge-bitcoin"><a href="https://bevry.me/bitcoin" title="Donate once-off to this project using Bitcoin"><img src="https://img.shields.io/badge/bitcoin-donate-yellow.svg" alt="Bitcoin donate button" /></a></span>
+<span class="badge-wishlist"><a href="https://bevry.me/wishlist" title="Buy an item on our wishlist for us"><img src="https://img.shields.io/badge/wishlist-donate-yellow.svg" alt="Wishlist browse button" /></a></span>
+<br class="badge-separator" />
+<span class="badge-slackin"><a href="https://slack.bevry.me" title="Join this project's slack community"><img src="https://slack.bevry.me/badge.svg" alt="Slack community badge" /></a></span>
+
+<!-- /BADGES -->
+
+
+<!-- DESCRIPTION/ -->
+
+Publish multiple editions for your JavaScript packages consistently and easily (e.g. source edition, esnext edition, es2015 edition)
+
+<!-- /DESCRIPTION -->
+
+
+## Discover
+
+[Watch the talk.](https://youtu.be/IAB8_UlcNWI)
+
+[Get started with the guides and explanations.](https://github.com/bevry/editions/wiki)
+
+[View the API documentation.](http://master.editions.bevry.surge.sh/docs/)
+
+
+<!-- HISTORY/ -->
+
+<h2>History</h2>
+
+<a href="https://github.com/bevry/editions/blob/master/HISTORY.md#files">Discover the release history by heading on over to the <code>HISTORY.md</code> file.</a>
+
+<!-- /HISTORY -->
+
+
+<!-- CONTRIBUTE/ -->
+
+<h2>Contribute</h2>
+
+<a href="https://github.com/bevry/editions/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a>
+
+<!-- /CONTRIBUTE -->
+
+
+<!-- BACKERS/ -->
+
+<h2>Backers</h2>
+
+<h3>Maintainers</h3>
+
+These amazing people are maintaining this project:
+
+<ul><li><a href="http://balupton.com">Benjamin Lupton</a> — <a href="https://github.com/bevry/editions/commits?author=balupton" title="View the GitHub contributions of Benjamin Lupton on repository bevry/editions">view contributions</a></li></ul>
+
+<h3>Sponsors</h3>
+
+No sponsors yet! Will you be the first?
+
+<span class="badge-patreon"><a href="https://patreon.com/bevry" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
+<span class="badge-opencollective"><a href="https://opencollective.com/bevry" title="Donate to this project using Open Collective"><img src="https://img.shields.io/badge/open%20collective-donate-yellow.svg" alt="Open Collective donate button" /></a></span>
+<span class="badge-gratipay"><a href="https://www.gratipay.com/bevry" title="Donate weekly to this project using Gratipay"><img src="https://img.shields.io/badge/gratipay-donate-yellow.svg" alt="Gratipay donate button" /></a></span>
+<span class="badge-flattr"><a href="https://flattr.com/profile/balupton" title="Donate to this project using Flattr"><img src="https://img.shields.io/badge/flattr-donate-yellow.svg" alt="Flattr donate button" /></a></span>
+<span class="badge-paypal"><a href="https://bevry.me/paypal" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" alt="PayPal donate button" /></a></span>
+<span class="badge-bitcoin"><a href="https://bevry.me/bitcoin" title="Donate once-off to this project using Bitcoin"><img src="https://img.shields.io/badge/bitcoin-donate-yellow.svg" alt="Bitcoin donate button" /></a></span>
+<span class="badge-wishlist"><a href="https://bevry.me/wishlist" title="Buy an item on our wishlist for us"><img src="https://img.shields.io/badge/wishlist-donate-yellow.svg" alt="Wishlist browse button" /></a></span>
+
+<h3>Contributors</h3>
+
+These amazing people have contributed code to this project:
+
+<ul><li><a href="http://balupton.com">Benjamin Lupton</a> — <a href="https://github.com/bevry/editions/commits?author=balupton" title="View the GitHub contributions of Benjamin Lupton on repository bevry/editions">view contributions</a></li>
+<li><a href="http://zdroid.github.io">Zlatan Vasović</a> — <a href="https://github.com/bevry/editions/commits?author=zdroid" title="View the GitHub contributions of Zlatan Vasović on repository bevry/editions">view contributions</a></li></ul>
+
+<a href="https://github.com/bevry/editions/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a>
+
+<!-- /BACKERS -->
+
+
+<!-- LICENSE/ -->
+
+<h2>License</h2>
+
+Unless stated otherwise all works are:
+
+<ul><li>Copyright &copy; 2016+ <a href="http://bevry.me">Bevry Pty Ltd</a></li></ul>
+
+and licensed under:
+
+<ul><li><a href="http://spdx.org/licenses/MIT.html">MIT License</a></li></ul>
+
+<!-- /LICENSE -->

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů