From 854bacf6049a6426cb96eaf67460a3849e0aa343 Mon Sep 17 00:00:00 2001 From: Menethil Date: Sat, 25 Aug 2018 10:19:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0Renard=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../litemall/core/system/BaseConfig.java | 1 + .../litemall/db/dao/StatMapper.java | 3 - .../litemall/db/service/StatService.java | 5 - renard-wx/app.js | 31 + renard-wx/app.json | 78 + renard-wx/app.wxss | 24 + renard-wx/components/capsule/index.js | 31 + renard-wx/components/capsule/index.json | 3 + renard-wx/components/capsule/index.wxml | 10 + renard-wx/components/capsule/index.wxss | 42 + renard-wx/components/good/good.js | 38 + renard-wx/components/good/good.json | 4 + renard-wx/components/good/good.wxml | 8 + renard-wx/components/good/good.wxss | 41 + renard-wx/components/goodList/goodList.js | 26 + renard-wx/components/goodList/goodList.json | 6 + renard-wx/components/goodList/goodList.wxml | 9 + renard-wx/components/goodList/goodList.wxss | 33 + renard-wx/components/icon/index.js | 16 + renard-wx/components/icon/index.json | 3 + renard-wx/components/icon/index.wxml | 7 + renard-wx/components/icon/index.wxss | 1 + renard-wx/components/notice-bar/index.js | 177 ++ renard-wx/components/notice-bar/index.json | 6 + renard-wx/components/notice-bar/index.wxml | 31 + renard-wx/components/notice-bar/index.wxss | 1 + renard-wx/components/notify/index.js | 69 + renard-wx/components/notify/index.json | 3 + renard-wx/components/notify/index.wxml | 6 + renard-wx/components/notify/index.wxss | 1 + renard-wx/components/sharePop/sharePop.js | 86 + renard-wx/components/sharePop/sharePop.json | 4 + renard-wx/components/sharePop/sharePop.wxml | 17 + renard-wx/components/sharePop/sharePop.wxss | 62 + renard-wx/config/api.js | 85 + renard-wx/images/Cool-love-o.png | Bin 0 -> 1547 bytes renard-wx/images/Cool-love.png | Bin 0 -> 2205 bytes renard-wx/images/Coupons-icon-f.png | Bin 0 -> 5196 bytes renard-wx/images/Coupons-icon-o.png | Bin 0 -> 3949 bytes renard-wx/images/Edit-icon.png | Bin 0 -> 1555 bytes renard-wx/images/Hot-cart.png | Bin 0 -> 3538 bytes renard-wx/images/Hot-love-o.png | Bin 0 -> 1978 bytes renard-wx/images/Hot-love.png | Bin 0 -> 3066 bytes renard-wx/images/Plaus-icon.png | Bin 0 -> 588 bytes renard-wx/images/about-icon.png | Bin 0 -> 3400 bytes renard-wx/images/confirm.png | Bin 0 -> 5100 bytes renard-wx/images/goods-chat.png | Bin 0 -> 3336 bytes renard-wx/images/goods-share.png | Bin 0 -> 2354 bytes renard-wx/images/my-icon-1.png | Bin 0 -> 1214 bytes renard-wx/images/my-icon-2.png | Bin 0 -> 1046 bytes renard-wx/images/my-icon-3.png | Bin 0 -> 1098 bytes renard-wx/images/my-icon-4.png | Bin 0 -> 1735 bytes renard-wx/images/right-icon.png | Bin 0 -> 1163 bytes renard-wx/images/tabbar/cart-f.png | Bin 0 -> 2019 bytes renard-wx/images/tabbar/cart-o.png | Bin 0 -> 3418 bytes renard-wx/images/tabbar/like-f.png | Bin 0 -> 2115 bytes renard-wx/images/tabbar/like-o.png | Bin 0 -> 2832 bytes renard-wx/images/tabbar/menu-f.png | Bin 0 -> 3100 bytes renard-wx/images/tabbar/menu-o.png | Bin 0 -> 3974 bytes renard-wx/images/tabbar/user-f.png | Bin 0 -> 4770 bytes renard-wx/images/tabbar/user-o.png | Bin 0 -> 7087 bytes renard-wx/lib/wxParse/html2json.js | 242 ++ renard-wx/lib/wxParse/htmlparser.js | 182 ++ renard-wx/lib/wxParse/showdown.js | 2529 +++++++++++++++++ renard-wx/lib/wxParse/wxDiscode.js | 206 ++ renard-wx/lib/wxParse/wxParse.js | 146 + renard-wx/lib/wxParse/wxParse.wxml | 928 ++++++ renard-wx/lib/wxParse/wxParse.wxss | 202 ++ renard-wx/pages/auth/login/login.js | 140 + renard-wx/pages/auth/login/login.json | 3 + renard-wx/pages/auth/login/login.wxml | 32 + renard-wx/pages/auth/login/login.wxss | 90 + renard-wx/pages/auth/register/register.js | 174 ++ renard-wx/pages/auth/register/register.json | 3 + renard-wx/pages/auth/register/register.wxml | 35 + renard-wx/pages/auth/register/register.wxss | 69 + renard-wx/pages/auth/reset/reset.js | 151 + renard-wx/pages/auth/reset/reset.json | 3 + renard-wx/pages/auth/reset/reset.wxml | 30 + renard-wx/pages/auth/reset/reset.wxss | 68 + renard-wx/pages/brand/brand.js | 59 + renard-wx/pages/brand/brand.json | 3 + renard-wx/pages/brand/brand.wxml | 14 + renard-wx/pages/brand/brand.wxss | 52 + renard-wx/pages/brandDetail/brandDetail.js | 69 + renard-wx/pages/brandDetail/brandDetail.json | 6 + renard-wx/pages/brandDetail/brandDetail.wxml | 20 + renard-wx/pages/brandDetail/brandDetail.wxss | 61 + renard-wx/pages/cart/cart.js | 305 ++ renard-wx/pages/cart/cart.json | 4 + renard-wx/pages/cart/cart.wxml | 59 + renard-wx/pages/cart/cart.wxss | 394 +++ renard-wx/pages/catalog/catalog.js | 82 + renard-wx/pages/catalog/catalog.json | 3 + renard-wx/pages/catalog/catalog.wxml | 32 + renard-wx/pages/catalog/catalog.wxss | 135 + renard-wx/pages/category/category.js | 140 + renard-wx/pages/category/category.json | 5 + renard-wx/pages/category/category.wxml | 19 + renard-wx/pages/category/category.wxss | 69 + renard-wx/pages/comment/comment.js | 124 + renard-wx/pages/comment/comment.json | 3 + renard-wx/pages/comment/comment.wxml | 33 + renard-wx/pages/comment/comment.wxss | 156 + renard-wx/pages/commentPost/commentPost.js | 179 ++ renard-wx/pages/commentPost/commentPost.json | 3 + renard-wx/pages/commentPost/commentPost.wxml | 55 + renard-wx/pages/commentPost/commentPost.wxss | 246 ++ renard-wx/pages/goods/goods.js | 627 ++++ renard-wx/pages/goods/goods.json | 8 + renard-wx/pages/goods/goods.wxml | 149 + renard-wx/pages/goods/goods.wxss | 752 +++++ .../groupon/grouponDetail/grouponDetail.js | 94 + .../groupon/grouponDetail/grouponDetail.json | 6 + .../groupon/grouponDetail/grouponDetail.wxml | 93 + .../groupon/grouponDetail/grouponDetail.wxss | 395 +++ .../pages/groupon/myGroupon/myGroupon.js | 61 + .../pages/groupon/myGroupon/myGroupon.json | 6 + .../pages/groupon/myGroupon/myGroupon.wxml | 52 + .../pages/groupon/myGroupon/myGroupon.wxss | 222 ++ renard-wx/pages/hotGoods/hotGoods.js | 131 + renard-wx/pages/hotGoods/hotGoods.json | 3 + renard-wx/pages/hotGoods/hotGoods.wxml | 40 + renard-wx/pages/hotGoods/hotGoods.wxss | 177 ++ renard-wx/pages/index/index.js | 139 + renard-wx/pages/index/index.json | 8 + renard-wx/pages/index/index.wxml | 140 + renard-wx/pages/index/index.wxss | 336 +++ renard-wx/pages/newGoods/newGoods.js | 123 + renard-wx/pages/newGoods/newGoods.json | 6 + renard-wx/pages/newGoods/newGoods.wxml | 32 + renard-wx/pages/newGoods/newGoods.wxss | 138 + renard-wx/pages/payResult/payResult.js | 63 + renard-wx/pages/payResult/payResult.json | 4 + renard-wx/pages/payResult/payResult.wxml | 23 + renard-wx/pages/payResult/payResult.wxss | 59 + renard-wx/pages/search/search.js | 199 ++ renard-wx/pages/search/search.json | 6 + renard-wx/pages/search/search.wxml | 59 + renard-wx/pages/search/search.wxss | 280 ++ renard-wx/pages/shopping/checkout/checkout.js | 178 ++ .../pages/shopping/checkout/checkout.json | 3 + .../pages/shopping/checkout/checkout.wxml | 65 + .../pages/shopping/checkout/checkout.wxss | 295 ++ renard-wx/pages/topic/topic.js | 94 + renard-wx/pages/topic/topic.json | 3 + renard-wx/pages/topic/topic.wxml | 16 + renard-wx/pages/topic/topic.wxss | 94 + renard-wx/pages/topicComment/topicComment.js | 116 + .../pages/topicComment/topicComment.json | 3 + .../pages/topicComment/topicComment.wxml | 22 + .../pages/topicComment/topicComment.wxss | 108 + .../topicCommentPost/topicCommentPost.js | 179 ++ .../topicCommentPost/topicCommentPost.json | 3 + .../topicCommentPost/topicCommentPost.wxml | 55 + .../topicCommentPost/topicCommentPost.wxss | 246 ++ renard-wx/pages/topicDetail/topicDetail.js | 97 + renard-wx/pages/topicDetail/topicDetail.json | 4 + renard-wx/pages/topicDetail/topicDetail.wxml | 50 + renard-wx/pages/topicDetail/topicDetail.wxss | 190 ++ renard-wx/pages/ucenter/address/address.js | 105 + renard-wx/pages/ucenter/address/address.json | 3 + renard-wx/pages/ucenter/address/address.wxml | 22 + renard-wx/pages/ucenter/address/address.wxss | 126 + .../pages/ucenter/addressAdd/addressAdd.js | 212 ++ .../pages/ucenter/addressAdd/addressAdd.json | 3 + .../pages/ucenter/addressAdd/addressAdd.wxml | 22 + .../pages/ucenter/addressAdd/addressAdd.wxss | 175 ++ renard-wx/pages/ucenter/collect/collect.js | 149 + renard-wx/pages/ucenter/collect/collect.json | 3 + renard-wx/pages/ucenter/collect/collect.wxml | 19 + renard-wx/pages/ucenter/collect/collect.wxss | 102 + renard-wx/pages/ucenter/coupon/coupon.js | 27 + renard-wx/pages/ucenter/coupon/coupon.json | 3 + renard-wx/pages/ucenter/coupon/coupon.wxml | 47 + renard-wx/pages/ucenter/coupon/coupon.wxss | 178 ++ .../pages/ucenter/footprint/footprint.js | 129 + .../pages/ucenter/footprint/footprint.json | 3 + .../pages/ucenter/footprint/footprint.wxml | 23 + .../pages/ucenter/footprint/footprint.wxss | 125 + renard-wx/pages/ucenter/index/index.js | 130 + renard-wx/pages/ucenter/index/index.json | 5 + renard-wx/pages/ucenter/index/index.wxml | 42 + renard-wx/pages/ucenter/index/index.wxss | 151 + renard-wx/pages/ucenter/order/order.js | 61 + renard-wx/pages/ucenter/order/order.json | 6 + renard-wx/pages/ucenter/order/order.wxml | 50 + renard-wx/pages/ucenter/order/order.wxss | 183 ++ .../pages/ucenter/orderDetail/orderDetail.js | 208 ++ .../ucenter/orderDetail/orderDetail.json | 3 + .../ucenter/orderDetail/orderDetail.wxml | 87 + .../ucenter/orderDetail/orderDetail.wxss | 337 +++ renard-wx/project.config.json | 216 ++ renard-wx/static/images/address-bg-bd.png | Bin 0 -> 183 bytes renard-wx/static/images/address_right.png | Bin 0 -> 283 bytes renard-wx/static/images/checkbox.png | Bin 0 -> 1421 bytes renard-wx/static/images/clear_input.png | Bin 0 -> 259 bytes renard-wx/static/images/del-address.png | Bin 0 -> 221 bytes renard-wx/static/images/detail_back.png | Bin 0 -> 195 bytes renard-wx/static/images/detail_kefu.png | Bin 0 -> 528 bytes renard-wx/static/images/edit-address.png | Bin 0 -> 287 bytes renard-wx/static/images/go.png | Bin 0 -> 254 bytes .../static/images/ic_menu_choice_nor.png | Bin 0 -> 1030 bytes .../static/images/ic_menu_choice_pressed.png | Bin 0 -> 1030 bytes renard-wx/static/images/ic_menu_me_nor.png | Bin 0 -> 1439 bytes .../static/images/ic_menu_me_pressed.png | Bin 0 -> 1425 bytes .../static/images/ic_menu_shoping_nor.png | Bin 0 -> 1394 bytes .../static/images/ic_menu_shoping_pressed.png | Bin 0 -> 1397 bytes renard-wx/static/images/ic_menu_sort_nor.png | Bin 0 -> 1565 bytes .../static/images/ic_menu_sort_pressed.png | Bin 0 -> 1574 bytes renard-wx/static/images/ic_menu_topic_nor.png | Bin 0 -> 388 bytes .../static/images/ic_menu_topic_pressed.png | Bin 0 -> 388 bytes renard-wx/static/images/icon_close.png | Bin 0 -> 219 bytes renard-wx/static/images/icon_collect.png | Bin 0 -> 1236 bytes .../static/images/icon_collect_checked.png | Bin 0 -> 1579 bytes renard-wx/static/images/icon_error.png | Bin 0 -> 1259 bytes renard-wx/static/images/icon_go_more.png | Bin 0 -> 736 bytes renard-wx/static/images/selnum.png | Bin 0 -> 1637 bytes renard-wx/static/images/wxpay.png | Bin 0 -> 1791 bytes renard-wx/utils/check.js | 12 + renard-wx/utils/user.js | 91 + renard-wx/utils/util.js | 95 + 222 files changed, 17928 insertions(+), 8 deletions(-) create mode 100644 renard-wx/app.js create mode 100644 renard-wx/app.json create mode 100644 renard-wx/app.wxss create mode 100644 renard-wx/components/capsule/index.js create mode 100644 renard-wx/components/capsule/index.json create mode 100644 renard-wx/components/capsule/index.wxml create mode 100644 renard-wx/components/capsule/index.wxss create mode 100644 renard-wx/components/good/good.js create mode 100644 renard-wx/components/good/good.json create mode 100644 renard-wx/components/good/good.wxml create mode 100644 renard-wx/components/good/good.wxss create mode 100644 renard-wx/components/goodList/goodList.js create mode 100644 renard-wx/components/goodList/goodList.json create mode 100644 renard-wx/components/goodList/goodList.wxml create mode 100644 renard-wx/components/goodList/goodList.wxss create mode 100644 renard-wx/components/icon/index.js create mode 100644 renard-wx/components/icon/index.json create mode 100644 renard-wx/components/icon/index.wxml create mode 100644 renard-wx/components/icon/index.wxss create mode 100644 renard-wx/components/notice-bar/index.js create mode 100644 renard-wx/components/notice-bar/index.json create mode 100644 renard-wx/components/notice-bar/index.wxml create mode 100644 renard-wx/components/notice-bar/index.wxss create mode 100644 renard-wx/components/notify/index.js create mode 100644 renard-wx/components/notify/index.json create mode 100644 renard-wx/components/notify/index.wxml create mode 100644 renard-wx/components/notify/index.wxss create mode 100644 renard-wx/components/sharePop/sharePop.js create mode 100644 renard-wx/components/sharePop/sharePop.json create mode 100644 renard-wx/components/sharePop/sharePop.wxml create mode 100644 renard-wx/components/sharePop/sharePop.wxss create mode 100644 renard-wx/config/api.js create mode 100644 renard-wx/images/Cool-love-o.png create mode 100644 renard-wx/images/Cool-love.png create mode 100644 renard-wx/images/Coupons-icon-f.png create mode 100644 renard-wx/images/Coupons-icon-o.png create mode 100644 renard-wx/images/Edit-icon.png create mode 100644 renard-wx/images/Hot-cart.png create mode 100644 renard-wx/images/Hot-love-o.png create mode 100644 renard-wx/images/Hot-love.png create mode 100644 renard-wx/images/Plaus-icon.png create mode 100644 renard-wx/images/about-icon.png create mode 100644 renard-wx/images/confirm.png create mode 100644 renard-wx/images/goods-chat.png create mode 100644 renard-wx/images/goods-share.png create mode 100644 renard-wx/images/my-icon-1.png create mode 100644 renard-wx/images/my-icon-2.png create mode 100644 renard-wx/images/my-icon-3.png create mode 100644 renard-wx/images/my-icon-4.png create mode 100644 renard-wx/images/right-icon.png create mode 100644 renard-wx/images/tabbar/cart-f.png create mode 100644 renard-wx/images/tabbar/cart-o.png create mode 100644 renard-wx/images/tabbar/like-f.png create mode 100644 renard-wx/images/tabbar/like-o.png create mode 100644 renard-wx/images/tabbar/menu-f.png create mode 100644 renard-wx/images/tabbar/menu-o.png create mode 100644 renard-wx/images/tabbar/user-f.png create mode 100644 renard-wx/images/tabbar/user-o.png create mode 100644 renard-wx/lib/wxParse/html2json.js create mode 100644 renard-wx/lib/wxParse/htmlparser.js create mode 100644 renard-wx/lib/wxParse/showdown.js create mode 100644 renard-wx/lib/wxParse/wxDiscode.js create mode 100644 renard-wx/lib/wxParse/wxParse.js create mode 100644 renard-wx/lib/wxParse/wxParse.wxml create mode 100644 renard-wx/lib/wxParse/wxParse.wxss create mode 100644 renard-wx/pages/auth/login/login.js create mode 100644 renard-wx/pages/auth/login/login.json create mode 100644 renard-wx/pages/auth/login/login.wxml create mode 100644 renard-wx/pages/auth/login/login.wxss create mode 100644 renard-wx/pages/auth/register/register.js create mode 100644 renard-wx/pages/auth/register/register.json create mode 100644 renard-wx/pages/auth/register/register.wxml create mode 100644 renard-wx/pages/auth/register/register.wxss create mode 100644 renard-wx/pages/auth/reset/reset.js create mode 100644 renard-wx/pages/auth/reset/reset.json create mode 100644 renard-wx/pages/auth/reset/reset.wxml create mode 100644 renard-wx/pages/auth/reset/reset.wxss create mode 100644 renard-wx/pages/brand/brand.js create mode 100644 renard-wx/pages/brand/brand.json create mode 100644 renard-wx/pages/brand/brand.wxml create mode 100644 renard-wx/pages/brand/brand.wxss create mode 100644 renard-wx/pages/brandDetail/brandDetail.js create mode 100644 renard-wx/pages/brandDetail/brandDetail.json create mode 100644 renard-wx/pages/brandDetail/brandDetail.wxml create mode 100644 renard-wx/pages/brandDetail/brandDetail.wxss create mode 100644 renard-wx/pages/cart/cart.js create mode 100644 renard-wx/pages/cart/cart.json create mode 100644 renard-wx/pages/cart/cart.wxml create mode 100644 renard-wx/pages/cart/cart.wxss create mode 100644 renard-wx/pages/catalog/catalog.js create mode 100644 renard-wx/pages/catalog/catalog.json create mode 100644 renard-wx/pages/catalog/catalog.wxml create mode 100644 renard-wx/pages/catalog/catalog.wxss create mode 100644 renard-wx/pages/category/category.js create mode 100644 renard-wx/pages/category/category.json create mode 100644 renard-wx/pages/category/category.wxml create mode 100644 renard-wx/pages/category/category.wxss create mode 100644 renard-wx/pages/comment/comment.js create mode 100644 renard-wx/pages/comment/comment.json create mode 100644 renard-wx/pages/comment/comment.wxml create mode 100644 renard-wx/pages/comment/comment.wxss create mode 100644 renard-wx/pages/commentPost/commentPost.js create mode 100644 renard-wx/pages/commentPost/commentPost.json create mode 100644 renard-wx/pages/commentPost/commentPost.wxml create mode 100644 renard-wx/pages/commentPost/commentPost.wxss create mode 100644 renard-wx/pages/goods/goods.js create mode 100644 renard-wx/pages/goods/goods.json create mode 100644 renard-wx/pages/goods/goods.wxml create mode 100644 renard-wx/pages/goods/goods.wxss create mode 100644 renard-wx/pages/groupon/grouponDetail/grouponDetail.js create mode 100644 renard-wx/pages/groupon/grouponDetail/grouponDetail.json create mode 100644 renard-wx/pages/groupon/grouponDetail/grouponDetail.wxml create mode 100644 renard-wx/pages/groupon/grouponDetail/grouponDetail.wxss create mode 100644 renard-wx/pages/groupon/myGroupon/myGroupon.js create mode 100644 renard-wx/pages/groupon/myGroupon/myGroupon.json create mode 100644 renard-wx/pages/groupon/myGroupon/myGroupon.wxml create mode 100644 renard-wx/pages/groupon/myGroupon/myGroupon.wxss create mode 100644 renard-wx/pages/hotGoods/hotGoods.js create mode 100644 renard-wx/pages/hotGoods/hotGoods.json create mode 100644 renard-wx/pages/hotGoods/hotGoods.wxml create mode 100644 renard-wx/pages/hotGoods/hotGoods.wxss create mode 100644 renard-wx/pages/index/index.js create mode 100644 renard-wx/pages/index/index.json create mode 100644 renard-wx/pages/index/index.wxml create mode 100644 renard-wx/pages/index/index.wxss create mode 100644 renard-wx/pages/newGoods/newGoods.js create mode 100644 renard-wx/pages/newGoods/newGoods.json create mode 100644 renard-wx/pages/newGoods/newGoods.wxml create mode 100644 renard-wx/pages/newGoods/newGoods.wxss create mode 100644 renard-wx/pages/payResult/payResult.js create mode 100644 renard-wx/pages/payResult/payResult.json create mode 100644 renard-wx/pages/payResult/payResult.wxml create mode 100644 renard-wx/pages/payResult/payResult.wxss create mode 100644 renard-wx/pages/search/search.js create mode 100644 renard-wx/pages/search/search.json create mode 100644 renard-wx/pages/search/search.wxml create mode 100644 renard-wx/pages/search/search.wxss create mode 100644 renard-wx/pages/shopping/checkout/checkout.js create mode 100644 renard-wx/pages/shopping/checkout/checkout.json create mode 100644 renard-wx/pages/shopping/checkout/checkout.wxml create mode 100644 renard-wx/pages/shopping/checkout/checkout.wxss create mode 100644 renard-wx/pages/topic/topic.js create mode 100644 renard-wx/pages/topic/topic.json create mode 100644 renard-wx/pages/topic/topic.wxml create mode 100644 renard-wx/pages/topic/topic.wxss create mode 100644 renard-wx/pages/topicComment/topicComment.js create mode 100644 renard-wx/pages/topicComment/topicComment.json create mode 100644 renard-wx/pages/topicComment/topicComment.wxml create mode 100644 renard-wx/pages/topicComment/topicComment.wxss create mode 100644 renard-wx/pages/topicCommentPost/topicCommentPost.js create mode 100644 renard-wx/pages/topicCommentPost/topicCommentPost.json create mode 100644 renard-wx/pages/topicCommentPost/topicCommentPost.wxml create mode 100644 renard-wx/pages/topicCommentPost/topicCommentPost.wxss create mode 100644 renard-wx/pages/topicDetail/topicDetail.js create mode 100644 renard-wx/pages/topicDetail/topicDetail.json create mode 100644 renard-wx/pages/topicDetail/topicDetail.wxml create mode 100644 renard-wx/pages/topicDetail/topicDetail.wxss create mode 100644 renard-wx/pages/ucenter/address/address.js create mode 100644 renard-wx/pages/ucenter/address/address.json create mode 100644 renard-wx/pages/ucenter/address/address.wxml create mode 100644 renard-wx/pages/ucenter/address/address.wxss create mode 100644 renard-wx/pages/ucenter/addressAdd/addressAdd.js create mode 100644 renard-wx/pages/ucenter/addressAdd/addressAdd.json create mode 100644 renard-wx/pages/ucenter/addressAdd/addressAdd.wxml create mode 100644 renard-wx/pages/ucenter/addressAdd/addressAdd.wxss create mode 100644 renard-wx/pages/ucenter/collect/collect.js create mode 100644 renard-wx/pages/ucenter/collect/collect.json create mode 100644 renard-wx/pages/ucenter/collect/collect.wxml create mode 100644 renard-wx/pages/ucenter/collect/collect.wxss create mode 100644 renard-wx/pages/ucenter/coupon/coupon.js create mode 100644 renard-wx/pages/ucenter/coupon/coupon.json create mode 100644 renard-wx/pages/ucenter/coupon/coupon.wxml create mode 100644 renard-wx/pages/ucenter/coupon/coupon.wxss create mode 100644 renard-wx/pages/ucenter/footprint/footprint.js create mode 100644 renard-wx/pages/ucenter/footprint/footprint.json create mode 100644 renard-wx/pages/ucenter/footprint/footprint.wxml create mode 100644 renard-wx/pages/ucenter/footprint/footprint.wxss create mode 100644 renard-wx/pages/ucenter/index/index.js create mode 100644 renard-wx/pages/ucenter/index/index.json create mode 100644 renard-wx/pages/ucenter/index/index.wxml create mode 100644 renard-wx/pages/ucenter/index/index.wxss create mode 100644 renard-wx/pages/ucenter/order/order.js create mode 100644 renard-wx/pages/ucenter/order/order.json create mode 100644 renard-wx/pages/ucenter/order/order.wxml create mode 100644 renard-wx/pages/ucenter/order/order.wxss create mode 100644 renard-wx/pages/ucenter/orderDetail/orderDetail.js create mode 100644 renard-wx/pages/ucenter/orderDetail/orderDetail.json create mode 100644 renard-wx/pages/ucenter/orderDetail/orderDetail.wxml create mode 100644 renard-wx/pages/ucenter/orderDetail/orderDetail.wxss create mode 100644 renard-wx/project.config.json create mode 100644 renard-wx/static/images/address-bg-bd.png create mode 100644 renard-wx/static/images/address_right.png create mode 100644 renard-wx/static/images/checkbox.png create mode 100644 renard-wx/static/images/clear_input.png create mode 100644 renard-wx/static/images/del-address.png create mode 100644 renard-wx/static/images/detail_back.png create mode 100644 renard-wx/static/images/detail_kefu.png create mode 100644 renard-wx/static/images/edit-address.png create mode 100644 renard-wx/static/images/go.png create mode 100644 renard-wx/static/images/ic_menu_choice_nor.png create mode 100644 renard-wx/static/images/ic_menu_choice_pressed.png create mode 100644 renard-wx/static/images/ic_menu_me_nor.png create mode 100644 renard-wx/static/images/ic_menu_me_pressed.png create mode 100644 renard-wx/static/images/ic_menu_shoping_nor.png create mode 100644 renard-wx/static/images/ic_menu_shoping_pressed.png create mode 100644 renard-wx/static/images/ic_menu_sort_nor.png create mode 100644 renard-wx/static/images/ic_menu_sort_pressed.png create mode 100644 renard-wx/static/images/ic_menu_topic_nor.png create mode 100644 renard-wx/static/images/ic_menu_topic_pressed.png create mode 100644 renard-wx/static/images/icon_close.png create mode 100644 renard-wx/static/images/icon_collect.png create mode 100644 renard-wx/static/images/icon_collect_checked.png create mode 100644 renard-wx/static/images/icon_error.png create mode 100644 renard-wx/static/images/icon_go_more.png create mode 100644 renard-wx/static/images/selnum.png create mode 100644 renard-wx/static/images/wxpay.png create mode 100644 renard-wx/utils/check.js create mode 100644 renard-wx/utils/user.js create mode 100644 renard-wx/utils/util.js diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/system/BaseConfig.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/system/BaseConfig.java index 29b48509..41710c77 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/system/BaseConfig.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/system/BaseConfig.java @@ -9,6 +9,7 @@ import java.util.Map; * 配置基类,该类实际持有所有的配置,子类只是提供代理访问方法 */ abstract class BaseConfig { + //所有的配置均保存在该 HashMap 中 protected static Map configs = new HashMap<>(); diff --git a/litemall-db/src/main/java/org/linlinjava/litemall/db/dao/StatMapper.java b/litemall-db/src/main/java/org/linlinjava/litemall/db/dao/StatMapper.java index 4d57a60e..b1f65e92 100644 --- a/litemall-db/src/main/java/org/linlinjava/litemall/db/dao/StatMapper.java +++ b/litemall-db/src/main/java/org/linlinjava/litemall/db/dao/StatMapper.java @@ -1,8 +1,5 @@ package org.linlinjava.litemall.db.dao; -import org.apache.ibatis.annotations.Param; - -import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/litemall-db/src/main/java/org/linlinjava/litemall/db/service/StatService.java b/litemall-db/src/main/java/org/linlinjava/litemall/db/service/StatService.java index 0e39bde1..ee2eb098 100644 --- a/litemall-db/src/main/java/org/linlinjava/litemall/db/service/StatService.java +++ b/litemall-db/src/main/java/org/linlinjava/litemall/db/service/StatService.java @@ -1,12 +1,7 @@ package org.linlinjava.litemall.db.service; -import com.github.pagehelper.PageHelper; -import org.linlinjava.litemall.db.dao.LitemallUserMapper; import org.linlinjava.litemall.db.dao.StatMapper; -import org.linlinjava.litemall.db.domain.LitemallUser; -import org.linlinjava.litemall.db.domain.LitemallUserExample; import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; import javax.annotation.Resource; import java.util.List; diff --git a/renard-wx/app.js b/renard-wx/app.js new file mode 100644 index 00000000..e5cb4a08 --- /dev/null +++ b/renard-wx/app.js @@ -0,0 +1,31 @@ +var util = require('./utils/util.js'); +var api = require('./config/api.js'); +var user = require('./utils/user.js'); + +App({ + onLaunch: function() { + const updateManager = wx.getUpdateManager(); + wx.getUpdateManager().onUpdateReady(function() { + wx.showModal({ + title: '更新提示', + content: '新版本已经准备好,是否重启应用?', + success: function(res) { + if (res.confirm) { + // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 + updateManager.applyUpdate() + } + } + }) + }) + }, + onShow: function(options) { + user.checkLogin().then(res => { + this.globalData.hasLogin = true; + }).catch(() => { + this.globalData.hasLogin = false; + }); + }, + globalData: { + hasLogin: false + } +}) \ No newline at end of file diff --git a/renard-wx/app.json b/renard-wx/app.json new file mode 100644 index 00000000..90ed81c1 --- /dev/null +++ b/renard-wx/app.json @@ -0,0 +1,78 @@ +{ + "pages": [ + "pages/index/index", + "pages/catalog/catalog", + "pages/newGoods/newGoods", + "pages/hotGoods/hotGoods", + "pages/ucenter/index/index", + "pages/ucenter/address/address", + "pages/ucenter/addressAdd/addressAdd", + "pages/ucenter/footprint/footprint", + "pages/ucenter/order/order", + "pages/ucenter/orderDetail/orderDetail", + "pages/ucenter/coupon/coupon", + "pages/ucenter/collect/collect", + "pages/auth/login/login", + "pages/auth/register/register", + "pages/auth/reset/reset", + "pages/payResult/payResult", + "pages/comment/comment", + "pages/commentPost/commentPost", + "pages/topic/topic", + "pages/topicComment/topicComment", + "pages/topicDetail/topicDetail", + "pages/topicCommentPost/topicCommentPost", + "pages/brand/brand", + "pages/brandDetail/brandDetail", + "pages/search/search", + "pages/category/category", + "pages/cart/cart", + "pages/shopping/checkout/checkout", + "pages/goods/goods", + "pages/groupon/myGroupon/myGroupon", + "pages/groupon/grouponDetail/grouponDetail" + ], + "window": { + "navigationBarTitleText": "Renard", + "enablePullDownRefresh": true, + "backgroundTextStyle": "light", + "navigationBarBackgroundColor": "#fff", + "navigationBarTextStyle": "black" + }, + "tabBar": { + "color": "#6e6d6b", + "selectedColor": "#e64340", + "borderStyle": "white", + "backgroundColor": "#fff", + "box-shadow": "0 0 6px 0", + "list": [ + { + "pagePath": "pages/index/index", + "iconPath": "images/tabbar/like-f.png", + "selectedIconPath": "images/tabbar/like-o.png" + }, + { + "pagePath": "pages/catalog/catalog", + "iconPath": "images/tabbar/menu-f.png", + "selectedIconPath": "images/tabbar/menu-o.png" + }, + { + "pagePath": "pages/cart/cart", + "iconPath": "images/tabbar/cart-f.png", + "selectedIconPath": "images/tabbar/cart-o.png" + }, + { + "pagePath": "pages/ucenter/index/index", + "iconPath": "images/tabbar/user-f.png", + "selectedIconPath": "images/tabbar/user-o.png" + } + ] + }, + "networkTimeout": { + "request": 10000, + "connectSocket": 10000, + "uploadFile": 10000, + "downloadFile": 10000 + }, + "debug": true +} \ No newline at end of file diff --git a/renard-wx/app.wxss b/renard-wx/app.wxss new file mode 100644 index 00000000..fe9d0542 --- /dev/null +++ b/renard-wx/app.wxss @@ -0,0 +1,24 @@ +/**app.wxss**/ +.container { + box-sizing: border-box; + font-family: PingFangSC-Light,helvetica,'Heiti SC'; + width: 100%; + height: 100%; +} + +view,image,text,navigator{ + box-sizing: border-box; + padding:0; + margin:0; + +} + +view,text{ + font-family: PingFangSC-Light,helvetica,'Heiti SC'; + font-size: 29rpx; + color: #a78845; +} + +.wxParse-img { + display: block !important; +} \ No newline at end of file diff --git a/renard-wx/components/capsule/index.js b/renard-wx/components/capsule/index.js new file mode 100644 index 00000000..7b8452cc --- /dev/null +++ b/renard-wx/components/capsule/index.js @@ -0,0 +1,31 @@ +'use strict'; + +Component({ + externalClasses: ['custom-class'], + /** + * 组件的属性列表 + * 用于组件自定义设置 + */ + properties: { + // 颜色状态 + type: { + type: String, + value: '' + }, + // 自定义颜色 + color: { + type: String, + value: '' + }, + // 左侧内容 + leftText: { + type: String, + value: '' + }, + // 右侧内容 + rightText: { + type: String, + value: '' + } + } +}); \ No newline at end of file diff --git a/renard-wx/components/capsule/index.json b/renard-wx/components/capsule/index.json new file mode 100644 index 00000000..467ce294 --- /dev/null +++ b/renard-wx/components/capsule/index.json @@ -0,0 +1,3 @@ +{ + "component": true +} diff --git a/renard-wx/components/capsule/index.wxml b/renard-wx/components/capsule/index.wxml new file mode 100644 index 00000000..2c81021a --- /dev/null +++ b/renard-wx/components/capsule/index.wxml @@ -0,0 +1,10 @@ + + + {{ leftText }} + {{ rightText }} + + + {{ leftText }} + {{ rightText }} + + \ No newline at end of file diff --git a/renard-wx/components/capsule/index.wxss b/renard-wx/components/capsule/index.wxss new file mode 100644 index 00000000..4ba0eec5 --- /dev/null +++ b/renard-wx/components/capsule/index.wxss @@ -0,0 +1,42 @@ +.zan-capsule { + display: inline-block; + font-size: 12px; + vertical-align: middle; + line-height: 19px; + -webkit-transform: scale(0.83); + transform: scale(0.83); +} + +.zan-capsule__left, .zan-capsule__right { + display: inline-block; + line-height: 17px; + height: 19px; + vertical-align: middle; + box-sizing: border-box; +} + +.zan-capsule__left { + padding: 0 2px; + color: #fff; + background: #999; + border-radius: 2px 0 0 2px; + border: 1rpx solid #999; +} + +.zan-capsule__right { + padding: 0 5px; + color: #999; + border-radius: 0 2px 2px 0; + border: 1rpx solid #999; +} + +.zan-capsule--danger .zan-capsule__left { + color: #fff; + background: #f24544; + border-color: #f24544; +} + +.zan-capsule--danger .zan-capsule__right { + color: #f24544; + border-color: #f24544; +} diff --git a/renard-wx/components/good/good.js b/renard-wx/components/good/good.js new file mode 100644 index 00000000..84e67247 --- /dev/null +++ b/renard-wx/components/good/good.js @@ -0,0 +1,38 @@ +// components/good/good.js +Component({ + /** + * 组件的属性列表 + */ + properties: { + title: { // 属性名 + type: String, // 类型(必填),目前接受的类型包括:String, Number, Boolean, Object, Array, null(表示任意类型) + value: '标题' // 属性初始值(可选),如果未指定则会根据类型选择一个 + }, + imageUrl: { + type: String, + value: '' + }, + counterPrice: { + type: Number, + value: 0 + }, + retailPrice: { + type: Number, + value: 0 + }, + }, + + /** + * 组件的初始数据 + */ + data: { + + }, + + /** + * 组件的方法列表 + */ + methods: { + + } +}) \ No newline at end of file diff --git a/renard-wx/components/good/good.json b/renard-wx/components/good/good.json new file mode 100644 index 00000000..e8cfaaf8 --- /dev/null +++ b/renard-wx/components/good/good.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/renard-wx/components/good/good.wxml b/renard-wx/components/good/good.wxml new file mode 100644 index 00000000..0e0ce410 --- /dev/null +++ b/renard-wx/components/good/good.wxml @@ -0,0 +1,8 @@ + + + {{title}} + + 原价:¥{{counterPrice}} + 现价:¥{{retailPrice}} + + \ No newline at end of file diff --git a/renard-wx/components/good/good.wxss b/renard-wx/components/good/good.wxss new file mode 100644 index 00000000..0a6eb331 --- /dev/null +++ b/renard-wx/components/good/good.wxss @@ -0,0 +1,41 @@ +.good { + height: 480rpx; + width: 372rpx; + align-content: center; +} + +.good .img { + width: 302rpx; + height: 302rpx; + z-index: 1; +} + +.good .name { + text-align: center; + /* display: block; */ + width: 372rpx; + height: 35rpx; + margin-bottom: 14rpx; + overflow: hidden; + font-size: 28rpx; + color: #333; +} + +.good .price { + /* display: block; */ + text-align: center; + line-height: 30rpx; + font-size: 28rpx; + color: #a78845; +} + +.good .counterPrice { + text-decoration: line-through; + font-size: 24rpx; + color: #999; +} + +.good .retailPrice { + font-size: 24rpx; + color: #a78845; +} \ No newline at end of file diff --git a/renard-wx/components/goodList/goodList.js b/renard-wx/components/goodList/goodList.js new file mode 100644 index 00000000..a26c24fd --- /dev/null +++ b/renard-wx/components/goodList/goodList.js @@ -0,0 +1,26 @@ +// components/goodList/goodList.js +Component({ + /** + * 组件的属性列表 + */ + properties: { + goods: { // 属性名 + type: Array, // 类型(必填),目前接受的类型包括:String, Number, Boolean, Object, Array, null(表示任意类型) + value: [] // 属性初始值(可选),如果未指定则会根据类型选择一个 + }, + }, + + /** + * 组件的初始数据 + */ + data: { + + }, + + /** + * 组件的方法列表 + */ + methods: { + + } +}) \ No newline at end of file diff --git a/renard-wx/components/goodList/goodList.json b/renard-wx/components/goodList/goodList.json new file mode 100644 index 00000000..40bd86eb --- /dev/null +++ b/renard-wx/components/goodList/goodList.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "good": "../good/good" + } +} \ No newline at end of file diff --git a/renard-wx/components/goodList/goodList.wxml b/renard-wx/components/goodList/goodList.wxml new file mode 100644 index 00000000..43b12614 --- /dev/null +++ b/renard-wx/components/goodList/goodList.wxml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/renard-wx/components/goodList/goodList.wxss b/renard-wx/components/goodList/goodList.wxss new file mode 100644 index 00000000..fc7faf0c --- /dev/null +++ b/renard-wx/components/goodList/goodList.wxss @@ -0,0 +1,33 @@ +.b { + width: 750rpx; + padding: 0 6.25rpx; + height: auto; + overflow: hidden; + z-index: 1; +} + +.b .good { + z-index: 1; +} + +.b .item { + float: left; + background: #fff; + width: 365rpx; + margin-bottom: 6.25rpx; + height: 482rpx; + overflow: hidden; + text-align: center; + z-index: 1; +} + +.b .item .a { + height: 452rpx; + width: 100%; + z-index: 1; +} + +.b .item-b { + margin-left: 6.25rpx; + z-index: 1; +} diff --git a/renard-wx/components/icon/index.js b/renard-wx/components/icon/index.js new file mode 100644 index 00000000..8f2e645c --- /dev/null +++ b/renard-wx/components/icon/index.js @@ -0,0 +1,16 @@ +Component({ + externalClasses: ['custom-class'], + + properties: { + info: null, + name: String, + size: String, + color: String + }, + + methods: { + onClick() { + this.triggerEvent('click'); + } + } +}); diff --git a/renard-wx/components/icon/index.json b/renard-wx/components/icon/index.json new file mode 100644 index 00000000..467ce294 --- /dev/null +++ b/renard-wx/components/icon/index.json @@ -0,0 +1,3 @@ +{ + "component": true +} diff --git a/renard-wx/components/icon/index.wxml b/renard-wx/components/icon/index.wxml new file mode 100644 index 00000000..af2215ae --- /dev/null +++ b/renard-wx/components/icon/index.wxml @@ -0,0 +1,7 @@ + + {{ info }} + diff --git a/renard-wx/components/icon/index.wxss b/renard-wx/components/icon/index.wxss new file mode 100644 index 00000000..74caabfb --- /dev/null +++ b/renard-wx/components/icon/index.wxss @@ -0,0 +1 @@ +@font-face{font-style:normal;font-weight:400;font-family:vant-icon;src:url(https://img.yzcdn.cn/vant/vant-icon-eb8c95.ttf) format('truetype')}.van-icon{position:relative;display:inline-block;font:normal normal normal 14px/1 vant-icon;font-size:inherit;text-rendering:auto}.van-icon__info{color:#fff;left:100%;top:-.5em;font-size:.5em;padding:0 .3em;text-align:center;min-width:1.2em;line-height:1.2;position:absolute;border-radius:.6em;box-sizing:border-box;background-color:#f44;-webkit-transform:translateX(-50%);transform:translateX(-50%);font-family:PingFang SC,Helvetica Neue,Arial,sans-serif}.van-icon::before{display:inline-block}.van-icon-add-o::before{content:"\F000"}.van-icon-add::before{content:"\F001"}.van-icon-add2::before{content:"\F002"}.van-icon-after-sale::before{content:"\F003"}.van-icon-alipay::before{content:"\F004"}.van-icon-arrow-left::before{content:"\F005"}.van-icon-arrow::before{content:"\F006"}.van-icon-balance-pay::before{content:"\F007"}.van-icon-browsing-history::before{content:"\F008"}.van-icon-card::before{content:"\F009"}.van-icon-cart::before{content:"\F00A"}.van-icon-cash-back-record::before{content:"\F00B"}.van-icon-cash-on-deliver::before{content:"\F00C"}.van-icon-certificate::before{content:"\F00D"}.van-icon-chat::before{content:"\F00E"}.van-icon-check::before{content:"\F00F"}.van-icon-checked::before{content:"\F010"}.van-icon-clear::before{content:"\F011"}.van-icon-clock::before{content:"\F012"}.van-icon-close::before{content:"\F013"}.van-icon-completed::before{content:"\F014"}.van-icon-contact::before{content:"\F015"}.van-icon-coupon::before{content:"\F016"}.van-icon-credit-pay::before{content:"\F017"}.van-icon-debit-pay::before{content:"\F018"}.van-icon-delete::before{content:"\F019"}.van-icon-description::before{content:"\F01A"}.van-icon-discount::before{content:"\F01B"}.van-icon-ecard-pay::before{content:"\F01C"}.van-icon-edit-data::before{content:"\F01D"}.van-icon-edit::before{content:"\F01E"}.van-icon-exchange-record::before{content:"\F01F"}.van-icon-exchange::before{content:"\F020"}.van-icon-fail::before{content:"\F021"}.van-icon-free-postage::before{content:"\F022"}.van-icon-gift-card-pay::before{content:"\F023"}.van-icon-gift-card::before{content:"\F024"}.van-icon-gift::before{content:"\F025"}.van-icon-gold-coin::before{content:"\F026"}.van-icon-goods-collect::before{content:"\F027"}.van-icon-home::before{content:"\F028"}.van-icon-hot-sale::before{content:"\F029"}.van-icon-hot::before{content:"\F02A"}.van-icon-idcard::before{content:"\F02B"}.van-icon-info-o::before{content:"\F02C"}.van-icon-like-o::before{content:"\F02D"}.van-icon-like::before{content:"\F02E"}.van-icon-location::before{content:"\F02F"}.van-icon-logistics::before{content:"\F030"}.van-icon-more-o::before{content:"\F031"}.van-icon-more::before{content:"\F032"}.van-icon-new-arrival::before{content:"\F033"}.van-icon-new::before{content:"\F034"}.van-icon-other-pay::before{content:"\F035"}.van-icon-passed::before{content:"\F036"}.van-icon-password-not-view::before{content:"\F037"}.van-icon-password-view::before{content:"\F038"}.van-icon-pause::before{content:"\F039"}.van-icon-peer-pay::before{content:"\F03A"}.van-icon-pending-deliver::before{content:"\F03B"}.van-icon-pending-evaluate::before{content:"\F03C"}.van-icon-pending-orders::before{content:"\F03D"}.van-icon-pending-payment::before{content:"\F03E"}.van-icon-phone::before{content:"\F03F"}.van-icon-photo::before{content:"\F040"}.van-icon-photograph::before{content:"\F041"}.van-icon-play::before{content:"\F042"}.van-icon-point-gift::before{content:"\F043"}.van-icon-points-mall::before{content:"\F044"}.van-icon-points::before{content:"\F045"}.van-icon-qr-invalid::before{content:"\F046"}.van-icon-qr::before{content:"\F047"}.van-icon-question::before{content:"\F048"}.van-icon-receive-gift::before{content:"\F049"}.van-icon-records::before{content:"\F04A"}.van-icon-search::before{content:"\F04B"}.van-icon-send-gift::before{content:"\F04C"}.van-icon-setting::before{content:"\F04D"}.van-icon-share::before{content:"\F04E"}.van-icon-shop-collect::before{content:"\F04F"}.van-icon-shop::before{content:"\F050"}.van-icon-shopping-cart::before{content:"\F051"}.van-icon-sign::before{content:"\F052"}.van-icon-stop::before{content:"\F053"}.van-icon-success::before{content:"\F054"}.van-icon-tosend::before{content:"\F055"}.van-icon-underway::before{content:"\F056"}.van-icon-upgrade::before{content:"\F057"}.van-icon-value-card::before{content:"\F058"}.van-icon-wap-home::before{content:"\F059"}.van-icon-wap-nav::before{content:"\F05A"}.van-icon-warn::before{content:"\F05B"}.van-icon-wechat::before{content:"\F05C"} \ No newline at end of file diff --git a/renard-wx/components/notice-bar/index.js b/renard-wx/components/notice-bar/index.js new file mode 100644 index 00000000..c30faf36 --- /dev/null +++ b/renard-wx/components/notice-bar/index.js @@ -0,0 +1,177 @@ +const VALID_MODE = ['closeable', 'link']; +const FONT_COLOR = '#f60'; +const BG_COLOR = '#fff7cc'; + +Component({ + externalClasses: ['custom-class'], + + properties: { + text: { + type: String, + value: '', + observer() { + this.setData({}, this._init); + } + }, + mode: { + type: String, + value: '' + }, + url: { + type: String, + value: '' + }, + openType: { + type: String, + value: 'navigate' + }, + delay: { + type: Number, + value: 0 + }, + speed: { + type: Number, + value: 50 + }, + scrollable: { + type: Boolean, + value: true + }, + leftIcon: { + type: String, + value: '' + }, + color: { + type: String, + value: FONT_COLOR + }, + backgroundColor: { + type: String, + value: BG_COLOR + } + }, + + data: { + show: true, + hasRightIcon: false, + width: undefined, + wrapWidth: undefined, + elapse: undefined, + animation: null, + resetAnimation: null, + timer: null + }, + + attached() { + const { mode } = this.data; + if (mode && this._checkMode(mode)) { + this.setData({ + hasRightIcon: true + }); + } + }, + + detached() { + const { timer } = this.data; + timer && clearTimeout(timer); + }, + + methods: { + _checkMode(val) { + const isValidMode = ~VALID_MODE.indexOf(val); + if (!isValidMode) { + console.warn(`mode only accept value of ${VALID_MODE}, now get ${val}.`); + } + return isValidMode; + }, + + _init() { + wx.createSelectorQuery() + .in(this) + .select('.van-notice-bar__content') + .boundingClientRect((rect) => { + if (!rect || !rect.width) { + return; + } + this.setData({ + width: rect.width + }); + + wx.createSelectorQuery() + .in(this) + .select('.van-notice-bar__content-wrap') + .boundingClientRect((rect) => { + if (!rect || !rect.width) { + return; + } + + const wrapWidth = rect.width; + const { + width, speed, scrollable, delay + } = this.data; + + if (scrollable && wrapWidth < width) { + const elapse = width / speed * 1000; + const animation = wx.createAnimation({ + duration: elapse, + timeingFunction: 'linear', + delay + }); + const resetAnimation = wx.createAnimation({ + duration: 0, + timeingFunction: 'linear' + }); + + this.setData({ + elapse, + wrapWidth, + animation, + resetAnimation + }, () => { + this._scroll(); + }); + } + }) + .exec(); + }) + .exec(); + }, + + _scroll() { + const { + animation, resetAnimation, wrapWidth, elapse, speed + } = this.data; + resetAnimation.translateX(wrapWidth).step(); + const animationData = animation.translateX(-(elapse * speed) / 1000).step(); + this.setData({ + animationData: resetAnimation.export() + }); + setTimeout(() => { + this.setData({ + animationData: animationData.export() + }); + }, 100); + + const timer = setTimeout(() => { + this._scroll(); + }, elapse); + + this.setData({ + timer + }); + }, + + _handleButtonClick() { + const { timer } = this.data; + timer && clearTimeout(timer); + this.setData({ + show: false, + timer: null + }); + }, + + onClick(event) { + this.triggerEvent('click', event); + } + } +}); diff --git a/renard-wx/components/notice-bar/index.json b/renard-wx/components/notice-bar/index.json new file mode 100644 index 00000000..a9ab393a --- /dev/null +++ b/renard-wx/components/notice-bar/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "van-icon": "../icon/index" + } +} \ No newline at end of file diff --git a/renard-wx/components/notice-bar/index.wxml b/renard-wx/components/notice-bar/index.wxml new file mode 100644 index 00000000..511b6d66 --- /dev/null +++ b/renard-wx/components/notice-bar/index.wxml @@ -0,0 +1,31 @@ + + + + + + + {{ text }} + + + + + + + + + + diff --git a/renard-wx/components/notice-bar/index.wxss b/renard-wx/components/notice-bar/index.wxss new file mode 100644 index 00000000..549d026d --- /dev/null +++ b/renard-wx/components/notice-bar/index.wxss @@ -0,0 +1 @@ +.van-notice-bar{display:-webkit-box;display:-webkit-flex;display:flex;padding:9px 10px;font-size:12px;line-height:1.5}.van-notice-bar--within-icon{position:relative;padding-right:30px}.van-notice-bar__left-icon{height:18px;min-width:20px;padding-top:1px;box-sizing:border-box}.van-notice-bar__left-icon>image{width:16px;height:16px}.van-notice-bar__right-icon{position:absolute;top:10px;right:10px;font-size:15px;line-height:1}.van-notice-bar__content-wrap{position:relative;-webkit-box-flex:1;-webkit-flex:1;flex:1;height:18px;overflow:hidden}.van-notice-bar__content{position:absolute;white-space:nowrap} \ No newline at end of file diff --git a/renard-wx/components/notify/index.js b/renard-wx/components/notify/index.js new file mode 100644 index 00000000..de20e1a2 --- /dev/null +++ b/renard-wx/components/notify/index.js @@ -0,0 +1,69 @@ +Component({ + properties: { + text: String, + color: { + type: String, + value: '#fff' + }, + backgroundColor: { + type: String, + value: '#e64340' + }, + duration: { + type: Number, + value: 3000 + } + }, + + methods: { + show() { + const { duration } = this.data; + + clearTimeout(this.timer); + this.setData({ + show: true + }); + + if (duration > 0 && duration !== Infinity) { + this.timer = setTimeout(() => { + this.hide(); + }, duration); + } + }, + + hide() { + clearTimeout(this.timer); + this.setData({ + show: false + }); + } + } +}); + +const defaultOptions = { + selector: '#van-notify', + duration: 3000 +}; + +function Notify(options = {}) { + const pages = getCurrentPages(); + const ctx = pages[pages.length - 1]; + + options = Object.assign({}, defaultOptions, parseParam(options)); + + const el = ctx.selectComponent(options.selector); + delete options.selector; + + if (el) { + el.setData({ + ...options + }); + el.show(); + } +} + +function parseParam(params = '') { + return typeof params === 'object' ? params : { text: params }; +} + +module.exports = Notify; diff --git a/renard-wx/components/notify/index.json b/renard-wx/components/notify/index.json new file mode 100644 index 00000000..32640e0d --- /dev/null +++ b/renard-wx/components/notify/index.json @@ -0,0 +1,3 @@ +{ + "component": true +} \ No newline at end of file diff --git a/renard-wx/components/notify/index.wxml b/renard-wx/components/notify/index.wxml new file mode 100644 index 00000000..5fc9f597 --- /dev/null +++ b/renard-wx/components/notify/index.wxml @@ -0,0 +1,6 @@ + + {{ text }} + diff --git a/renard-wx/components/notify/index.wxss b/renard-wx/components/notify/index.wxss new file mode 100644 index 00000000..81be6eb5 --- /dev/null +++ b/renard-wx/components/notify/index.wxss @@ -0,0 +1 @@ +.van-notify{top:0;opacity:0;width:100%;z-index:110;color:#fff;position:fixed;min-height:32px;line-height:2.3;font-size:14px;text-align:center;background-color:#e64340;-webkit-transition:all .4s ease;transition:all .4s ease;-webkit-transform:translateZ(0) translateY(-100%);transform:translateZ(0) translateY(-100%)}.van-notify--show{opacity:1;-webkit-transform:translateZ(0) translateY(0);transform:translateZ(0) translateY(0)} \ No newline at end of file diff --git a/renard-wx/components/sharePop/sharePop.js b/renard-wx/components/sharePop/sharePop.js new file mode 100644 index 00000000..64098a77 --- /dev/null +++ b/renard-wx/components/sharePop/sharePop.js @@ -0,0 +1,86 @@ +// components/sharePop/sharePop.js +Component({ + /** + * 组件的属性列表 + */ + properties: { + url: { + type: String, + value: '', // 属性值 (可选) + }, + bottom:{ + type: Number, + value: 0, // 属性值 (可选) + } + }, + + /** + * 组件的初始数据 + */ + data: { + // 弹窗显示控制 + showPop: false, + }, + + /** + * 组件的方法列表 + */ + methods: { + closeShare: function() { + this.setData({ + showPop: false, + }); + }, + + togglePopup: function() { + let that = this; + this.setData({ + showPop: !this.data.showPop, + }); + }, + + // 保存分享图 + _saveShare: function() { + wx.showLoading({ + title: '加载中', + }); + + setTimeout(function() { + wx.hideLoading() + }, 2000); + + let that = this; + wx.downloadFile({ + url: that.data.url, + success: function(res) { + console.log(res) + wx.saveImageToPhotosAlbum({ + filePath: res.tempFilePath, + success: function(res) { + wx.showModal({ + title: '存图成功', + content: '图片成功保存到相册了,可以分享到朋友圈了', + showCancel: false, + confirmText: '好的', + confirmColor: '#a78845', + success: function(res) { + if (res.confirm) { + console.log('用户点击确定'); + } + } + }); + wx.hideLoading(); + }, + fail: function(res) { + console.log('fail') + } + }) + }, + fail: function() { + console.log('fail') + } + }) + }, + + } +}) \ No newline at end of file diff --git a/renard-wx/components/sharePop/sharePop.json b/renard-wx/components/sharePop/sharePop.json new file mode 100644 index 00000000..e8cfaaf8 --- /dev/null +++ b/renard-wx/components/sharePop/sharePop.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/renard-wx/components/sharePop/sharePop.wxml b/renard-wx/components/sharePop/sharePop.wxml new file mode 100644 index 00000000..49deba84 --- /dev/null +++ b/renard-wx/components/sharePop/sharePop.wxml @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/renard-wx/components/sharePop/sharePop.wxss b/renard-wx/components/sharePop/sharePop.wxss new file mode 100644 index 00000000..d4b3ba7c --- /dev/null +++ b/renard-wx/components/sharePop/sharePop.wxss @@ -0,0 +1,62 @@ +.share-pop-box { + width: 100%; + height: 100%; + position: fixed; + background: rgba(0, 0, 0, 0.5); + z-index: 8; + bottom: 0; + /* display: none; */ +} + +.share-pop { + width: 100%; + height: 400rpx; + max-height: 780rpx; + padding: 31.25rpx; + background: #fff; + position: fixed; + z-index: 9; + bottom: 0rpx; +} + +.share-pop .close { + position: absolute; + width: 48rpx; + height: 48rpx; + right: 31.25rpx; + overflow: hidden; + top: 31.25rpx; +} + +.share-pop .close .icon { + width: 48rpx; + height: 48rpx; +} + +.share-pop .share-items { + margin-top: 80rpx; + flex-flow: row; + display: flex; + justify-content: space-around; +} + +.share-pop .share-touser { + height: 160rpx; + width: 160rpx; + background: url("https://litemall-1256968571.file.myqcloud.com/icon/%E5%88%86%E4%BA%AB_%E5%BE%AE%E4%BF%A1.png") no-repeat center 1rpx; + background-size: 160rpx auto; + border-radius: 80rpx; +} + +.share-pop .share-friend { + height: 160rpx; + width: 160rpx; + background: url("https://litemall-1256968571.file.myqcloud.com/icon/%E5%88%86%E4%BA%AB_%E5%BE%AE%E4%BF%A1%E6%9C%8B%E5%8F%8B%E5%9C%88.png") no-repeat center 1rpx; + background-size: 160rpx auto; + border-radius: 10rpx; +} + +.share-pop image { + height: 120rpx; + width: 120rpx; +} \ No newline at end of file diff --git a/renard-wx/config/api.js b/renard-wx/config/api.js new file mode 100644 index 00000000..20579496 --- /dev/null +++ b/renard-wx/config/api.js @@ -0,0 +1,85 @@ +// 以下是业务服务器API地址 +// 本机开发时使用 +var WxApiRoot = 'http://192.168.1.105:8082/wx/'; +// 云平台上线时使用 +// var WxApiRoot = 'https://litemall.menethil.com.cn/wx/'; +// var WxApiRoot = 'https://www.menethil.com.cn/wx/'; + +module.exports = { + IndexUrl: WxApiRoot + 'home/index', //首页数据接口 + CatalogList: WxApiRoot + 'catalog/index', //分类目录全部分类数据接口 + CatalogCurrent: WxApiRoot + 'catalog/current', //分类目录当前分类数据接口 + + CatalogAll: WxApiRoot + 'catalog/all', //分类目录当前分类数据接口 + + AuthLoginByWeixin: WxApiRoot + 'auth/login_by_weixin', //微信登录 + AuthLoginByAccount: WxApiRoot + 'auth/login', //账号登录 + AuthRegister: WxApiRoot + 'auth/register', //账号注册 + AuthReset: WxApiRoot + 'auth/reset', //账号密码重置 + AuthRegisterCaptcha: WxApiRoot + 'auth/regCaptcha', //验证码 + + GoodsCount: WxApiRoot + 'goods/count', //统计商品总数 + GoodsList: WxApiRoot + 'goods/list', //获得商品列表 + GoodsCategory: WxApiRoot + 'goods/category', //获得分类数据 + GoodsDetail: WxApiRoot + 'goods/detail', //获得商品的详情 + GoodsNew: WxApiRoot + 'goods/new', //新品 + GoodsHot: WxApiRoot + 'goods/hot', //热门 + GoodsRelated: WxApiRoot + 'goods/related', //商品详情页的关联商品(大家都在看) + + BrandList: WxApiRoot + 'brand/list', //品牌列表 + BrandDetail: WxApiRoot + 'brand/detail', //品牌详情 + + CartList: WxApiRoot + 'cart/index', //获取购物车的数据 + CartAdd: WxApiRoot + 'cart/add', // 添加商品到购物车 + CartFastAdd: WxApiRoot + 'cart/fastadd', // 立即购买商品 + CartUpdate: WxApiRoot + 'cart/update', // 更新购物车的商品 + CartDelete: WxApiRoot + 'cart/delete', // 删除购物车的商品 + CartChecked: WxApiRoot + 'cart/checked', // 选择或取消选择商品 + CartGoodsCount: WxApiRoot + 'cart/goodscount', // 获取购物车商品件数 + CartCheckout: WxApiRoot + 'cart/checkout', // 下单前信息确认 + + CollectList: WxApiRoot + 'collect/list', //收藏列表 + CollectAddOrDelete: WxApiRoot + 'collect/addordelete', //添加或取消收藏 + + CommentList: WxApiRoot + 'comment/list', //评论列表 + CommentCount: WxApiRoot + 'comment/count', //评论总数 + CommentPost: WxApiRoot + 'comment/post', //发表评论 + + TopicList: WxApiRoot + 'topic/list', //专题列表 + TopicDetail: WxApiRoot + 'topic/detail', //专题详情 + TopicRelated: WxApiRoot + 'topic/related', //相关专题 + + SearchIndex: WxApiRoot + 'search/index', //搜索关键字 + SearchResult: WxApiRoot + 'search/result', //搜索结果 + SearchHelper: WxApiRoot + 'search/helper', //搜索帮助 + SearchClearHistory: WxApiRoot + 'search/clearhistory', //搜索历史清楚 + + AddressList: WxApiRoot + 'address/list', //收货地址列表 + AddressDetail: WxApiRoot + 'address/detail', //收货地址详情 + AddressSave: WxApiRoot + 'address/save', //保存收货地址 + AddressDelete: WxApiRoot + 'address/delete', //保存收货地址 + + RegionList: WxApiRoot + 'region/list', //获取区域列表 + + OrderSubmit: WxApiRoot + 'order/submit', // 提交订单 + OrderPrepay: WxApiRoot + 'order/prepay', // 订单的预支付会话 + OrderList: WxApiRoot + 'order/list', //订单列表 + OrderDetail: WxApiRoot + 'order/detail', //订单详情 + OrderCancel: WxApiRoot + 'order/cancel', //取消订单 + OrderRefund: WxApiRoot + 'order/refund', //退款取消订单 + OrderDelete: WxApiRoot + 'order/delete', //删除订单 + OrderConfirm: WxApiRoot + 'order/confirm', //确认收货 + OrderComment: WxApiRoot + 'order/comment', // 代评价商品信息 + + FootprintList: WxApiRoot + 'footprint/list', //足迹列表 + FootprintDelete: WxApiRoot + 'footprint/delete', //删除足迹 + + UserFormIdCreate: WxApiRoot + 'formid/create', //用户FromId,用于发送模版消息 + + GroupOn: WxApiRoot + 'groupon/query', //团购API-查询 + GroupOnMy: WxApiRoot + 'groupon/my', //团购API-我的团购 + GroupOnDetail: WxApiRoot + 'groupon/detail', //团购API-详情 + GroupOnJoin: WxApiRoot + 'groupon/join', //团购API-详情 + + StorageUpload: WxApiRoot + 'storage/upload' //图片上传 +}; \ No newline at end of file diff --git a/renard-wx/images/Cool-love-o.png b/renard-wx/images/Cool-love-o.png new file mode 100644 index 0000000000000000000000000000000000000000..086aee6e22a85791d08834601466b67848cb08ca GIT binary patch literal 1547 zcmV+m2K4!fP))~d7>Dsky+G9yRGA(i>ItHppz^XSE4%L8oFLAwyV$EPEpNK5eS*j*h`sMj=o5rG z0lRp`T6SfN009D|{=XSbl8H!y!ix`rAP6ENA|fIpA|fIpA|fIpA|fIpA|ldzBbP5% zRTV28Lo@|mZ~mMDlg;O;TcF(hxdLuB|MoT-HP6-*0q_Qx0b}fksTEtFJM0JPlQHlM zc5d^{zkS^TOW+cZV8|^1CRnD=dC2%CN~}bzfNLx;ax?>uX`ku4lvp5q#32w|0>DXC zRW6@#RaF;NIG?MkI(AG$%+GOERbK<1-%Gw#4(A??!;f>aNp!3q<3nWuHuQ{t>hG$- zd8Z73X=Np~3w0oLmGQGGUB++wQtb%>7a{uqQ{Xe<+rfPb;2qXih+zVJ!rJ&;+yZZT zreEOF2ly^ff_yT+dh&PQx@)pI_J!l|n>Yc!kXPnIz`_=y21r%%cPW4)+FwFWuz;Gi z+)r#vtoD1P>lz?N$@4YAO6-Rg`J?nTZloq$N1T=6{O z69CQ!kgPpm6-YWKIEa;M4>R&&uV?t|NhC>`tKMYZUWtUA9W6v-H`>cUG{*1M+gi)?L6Z9tnnh8?+AzT0y*n-9q-!N+k%$w6WA6q0809~%2uCT z6Lwa)p|L zQ0|CWpklu>XobhRHS(V|Hqparwr@@^jZ%N0oyP6{w$y!qZ6*1=Y@sjC>0r)9SMLp6 zU<))#Gmqx@xyq>qs4dYAqo(fYu-5GI@$dx-f_?6=k{@iScb^omj$NTEgI(6x8h=IG z9p%^rY@4Gei=L`4_lm9$oFV{9>|_-L#_L)?lvGQ^CyKo2M5-a(g z4e~URTL6F!8y_*O<>=1IbdpSpk-(-V*xj(#T^=;BdJ6Zl%Y)`V64+FMwU>SE9NuH; zU#3mQ%W@6?c$i=KK5;r+-H=gF|0THxK6R-&?%K zZgi~@r(-aEfL-b)A-Ai9Ypm0{N}CLKeG7ov71qW&uZ|tq8tb3T=}DFUR9^!C9E{|4)W6p{8H#E?6I z`_z4d(2G?Js?={AS`Dymf{ntpoq&2x;6)i?JLDQ*TY;6>Bm4%C?t!=TxQE}wkOQE0 zh1CWhX?C@+-h6jL53lQ84Y>)}WsI$8cJj>sUt)LfdR^kx1U#?AGWZpb?=s$C_Zk-T xJo80FL_|bHL_|bHL_|bHL_|bHL`38O{R`>KlM$il=ZXLT002ovPDHLkV1k)3*_QwS literal 0 HcmV?d00001 diff --git a/renard-wx/images/Cool-love.png b/renard-wx/images/Cool-love.png new file mode 100644 index 0000000000000000000000000000000000000000..04d46a6968b54a17c0c4006ae9865bb850118b4c GIT binary patch literal 2205 zcmaKui9geg1INFcZLZu_u3V84b0#^KW4Wiv8RijExt7^)^k`*sCfCCc+njTo9#QFW z#jKceSC1oP)Z-~9u{)P8LjI*tXkemWQ}T^CU542(w$TSl=$*20 z&jpjs_-YC|;c=Y#qpo{9{w0693UCeL6fD5b)I6+4dz@5|y&5pcT6+<4sPxt#PLd@8 zOSv8aw(FvGuw#2BG4YNlC|6M{?P4~cF=xkVtsEviYpRgIRRuIsdezlX1oUBya#<9| zIN{g=YIAvmM`-`!RU7{+l}H5LF4-Z?F@StBwnqu?czn&g640N^mW%fgXw*kmFX?ym zNy(zPlq*WgeCxmwarm_;%d_E$I7ONTAMX*nE=FAT#wl@|t735QFWk-jzVEal;4qP` zLuCspWbbC0myn1Lhd}CaIGKqA6E2tqTP^G0wDEm?M}Gs7_OE`Ia23*6#7bLPA)!$| zb4)kSzpGGg;w}^SIEiDYk%4vl|D8flWTcD^M3W8AnNXN7Lce+JE#+9QnJNCgk)#x2c?Z zDey^j#vl*Sw+5Rl+4Q^izUi=uQ8mrU%EqE?Ueh*wYK0Y?RL2Qxnd(4Mx=`-!bc2za zL!bQ8<*Y1HZfE+sAG1Nf{qxaAs!E)j6pemh9AKoP-u983{-Kf>y1_fyUAv05^jsYM zM$&^|LR6p9l&_D{cL+;TWP6KfLu~DuRD0^!&kh+d#+|m6@Gv*0$lh3*?A%YH&`cKR z3S1zd#jug!x!3j&d16x2Y3Rl1*_AWJvhu0~xIA8(rNl?b3a!uOD_+PsX?$NNX}`Y9 zJObFRsN|}Q+1|kgB<&8`*084gNI|JXyx}tqS8LYp!Ya#snBl*_oA-W;w%m%qNi$%W ztuar&VtA^YSxi)5^BRcZh?ddpUJz8^X!}A-9Us5lxTbfe^n@kYh`-TRms4hN#hF5c z#~~Q^GT6Hn7Dv5-j;7fyYVDO<;!>q$-*>cT9+yTRGXN&-A~(nv`2ThXTAnCnIEh4k z<$`(dU*y}Y;-3x4`L90DU@!%t3dumBu{sD?<3Tj%KmLqn;9Dl#0aMQYLa#xL=&VXd zbxxw%)>i4IlWQ}#>noGKMwLN0O}7p^L)lmDNanrnfXP)YTY$A+F`%9uQtyrs!=DG4 z$kR-ajxrg4eRA{!eV~RYTP>D3gkqCDb*#NXOm7Q_xESuU9ule>?2_!C8IhPBWk5^; zGzXd(ebtay{6N#&t?R0B|1i9beo{a@8EXi}db%1BtgHq7aO!FNo?a3Wnx8u4suj(; zCAb(ej_>75t`~I%+RsL`B~vNl6@VXnOmXCF45BVRc@#KrT)uA{PY_Bh9rit!j3o5m zDjl2;voZ7BDT0J7;4)=}xX1bmZeF-KiWy^;!y0=8N|SzP-vDPk*DZ#a`Pg^Y)P@CW zB!n4`A(Wfq#EdZ2pG9n(12!6MF@jB`R`RF25}Os6*h64l!rXdY&l`j_qK7VIS(fSf z-maf!y*dz90*_mE?pSS0Qt0qa-dTC2OLL(G&C+|!W#PQ@|Ad4{-uZnw>;YpBA0`Fd z(O$wN6^U6eeBFVKV`Ud@tLz-ia4 zfV+K-*3XiV1MQwXZN@0<6KIFgQfyhs&QsXy$UXsCL5q-rF;JdiBhG8I_1+Bi#l%cJ zbnc76z|wFHWXiVGRABa8)eJUFC~VM6<^$QNNou?joAac+?Z#xSf6neDamKdcXbTy- zY19cTxgRMsU|rCEbR(nWToe=J?LdwG>b;y7ju;O*6jpU+rngwzk>4LDhtDshG`n<5 z+j2Z~b(Aj}afWTC_UHE^Rx$@hAOyYe37A5DAeEd-aZ>?Mf7R=ZOA`rUS*a&H%%$5pJPiSE%G5NQup+yJjte)^PWD+PZt#S z_`a~pM`%3He=%>w>Q>Q9_MMq9Ca)F>GzjQZ|03cFT%xcsbjs&^`PJC2cCiYKR9D2u zLhfZ00j`2Tbp3ojBEwnI_6C%ZL!2ay@6nn|!&%~wMw0YB+Vc=V)-xEH$q$zz7!wi(P zkPo{8Qy21-{}S||=MMagsKz-!Jin^w2CQ9TUjli5tb61X{cyd%9Bxq{TWK9^Hr$-Y zP91+fj(Kk;N=1f%2nIb>xx;w-2zR`LmZHx*fwgHbP&!cYRwc*p&7hWV_RBw?)JBEv z`?6~VN<~8Pbs~y2oy@^E9jzJx8!{e-owXS#26aZYWOWB*n7FHE3q~?p7ac? zW4JpxYkGcVOcP15-6J|TruJti#gn4=6>q-}_lQhE&#!gWQStp|Kd={H$dx(@6?>Tw z`G3ShylL{OuJxeS6_)x>;SZ+yi#vjTkDszB`ZKwK$1VC!HXH4T@;ac}`}3cq`^-HC z>48Qdk{VY;%k5b@jupCf0z20Ysj2OfJ&@>hwv2_~u&(~IG#vqo^1o21d<6Opl(Oj< T?9lovI{^F3&Zv4Tf6D&=6iWNk literal 0 HcmV?d00001 diff --git a/renard-wx/images/Coupons-icon-f.png b/renard-wx/images/Coupons-icon-f.png new file mode 100644 index 0000000000000000000000000000000000000000..0189415473513863c4679fecfc98c3b1f95ec01a GIT binary patch literal 5196 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 FV1mLRyj%bP literal 0 HcmV?d00001 diff --git a/renard-wx/images/Hot-cart.png b/renard-wx/images/Hot-cart.png new file mode 100644 index 0000000000000000000000000000000000000000..b73f3f0b5932ca2610e4bafdfc687e165a9bdd17 GIT binary patch literal 3538 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 G(*FThWTUA7 literal 0 HcmV?d00001 diff --git a/renard-wx/images/Hot-love.png b/renard-wx/images/Hot-love.png new file mode 100644 index 0000000000000000000000000000000000000000..6b4b9492ae9bc05c4c8596e15a0e43873668911a GIT binary patch literal 3066 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 F@_*7$s}2AF literal 0 HcmV?d00001 diff --git a/renard-wx/images/Plaus-icon.png b/renard-wx/images/Plaus-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..83bc6254e605bb8956db84ccc9842be8ad6e9247 GIT binary patch literal 588 zcmeAS@N?(olHy`uVBq!ia0vp^z97uO1|%PIzTCjTz$EPH;uumf=k0A@zuN&KM?V$| zUSMS2#8So}^ua-bbF+ZT4aeQW(;5V%6Ie5sE;;s|^P%h(?*_v|(*rY~ewgaq6~E;3 z?4y@9=l|QZ)}=5f$Y60*){t_%$kF9?!YpcPP-LiicJ29(b95H>zS@-;Ex(QX`~Fo^PVIaC z@{}G>I&;HU`}Mcmzh%$=Y@@zAL--6^ZU2R-^;>u}A!Y<%8jC8F_$Y97W>4}RgM`ws z!we0(*O!~E{nzs`VcKzqhP?uH8?s+!EM9HVvbdy-{r&cbeeXBld%2=-dzyugQ^vAb z@7Q^vUP~pt(sK29Bz83aTH4)pZP$IH*P9-jeT`(BFg;z+$++($OJ8!@UuiamfA_A- zlp6%(zYFb4dwTkjmJJqjX8dqVNSpoGs_xL@EuWK&S|L&5Yj<2ZTDtJC#x>t5z*NHE>FVdQ&MBb@ E03v4unE(I) literal 0 HcmV?d00001 diff --git a/renard-wx/images/about-icon.png b/renard-wx/images/about-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ab7408f3428505404e42b68fe470170e9c16cf2a GIT binary patch literal 3400 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 e$SC#u#{U5mub(}eX|`ej0000 zd301&g2umlUoEMmvL}QDvLgYKAUi5s2o+ScM;&Do+tINdy61GyjAOU7PWMQ+LV}?7 zGN+f*=h(;Tk!VyD5L^H|lAREC!XC0Gl|8AXQrmlX{=mdarLt6|s$M1ae*Zw-diUNh zb-#PxeRq2#Hz&&ifP;ijLIGwHTZDvoPymtvd>9HwBm?*`(k;Npp&(KV;LA|PSijgJ zB*cRfkWR5hNQegoSi9IFB*cR<0?7bA3?+~Z;LA`Z8NjEJS^yu15`i^|EkZ&(C>U2Q zwg?IFp#5gkO|eBthzEUkXTE_kX)M53p?Cfv)uO795RGpQz^X-6At4&y8UR(Is*n&3 z3IK$Wi((6r5QjSkpq2|_3y}~93IN@qJ(Y|>5L=9d=b-?weS3wO0F`2kk?=fp@PC9! zw*eny&;ShNyqH2H#9`0?gt1d%3Xu?pK?4|!mQtxB=z9zr;Mtw|213{wF~vxD9@&sj zj1+@liAY*RBPcwsW?F1|vpRH!Q5_m1aGac|0>e3h=b53eQ{XK|rt7HZdfIDMt#y}X z^z~??#n$X9h8h4uF+l%K94VfIax&Aj*!kBJ)7Q5}B`gY7_^Bf(l`R86#)xVZcypsc z-(6mRy-;6s`9ySk)3w>2(CjXT{K2`euaAX{)ruv?-N+QGzS)Z&Id|9MNBval&=i2E zCz2jxtgW}J@y37ekDziJO~Iv3$de!JJGv%g)z&qrUrE-L-5+Z+bhqA)VFE83DUGqvm6?}5@IoPDLbtB7 zVY;hyIE(!jVC}3XP`u!2F%u;^Y3apf55AaSQ}a!?&VOE1cwpxmi`n2mV&n;8=<5i( zed#Op^rR)H70LkP$&Y?vY`$HQZhZ}(<6thf_{& zT(fakX;l1zH3MOSV5uwk_|>kas`B*VDR~Au(iemXd7cz9F$lZwA6~g?+bh3)=~#&; z3m*V@v(ewS@CoHCwuKJ>lyad8Keebw^o(c#X5l{cq>zaaH+RjM$k_Cy)?r<(RYm1r zynUZong$@0vt(?_F0w6r##qa#&)=@Bsr>t5v9tlB7+~u!zN!O5nJ2|K1Lfq#g_%DD zfWgOB-eSC2@X@PD0&f{%z{WFBPHtSgImce%Yfl~BS5tN2i)CVI8(>5O0F&c3keHTr zPNfdB9^N%yEO@s?>=`@C$xLfE=af#1yW6(#Tg#5TQFr^&v85x$^(3PjfKq8M>3lLKXv>c5^_rSLz^DcQ z049*XctVIXB&KCGSP%7D%!aC}3tyy)rH68IW5(v3Q^O0NX-hxbQ}0bRe}FL!u_u7B-Sh(r80BomN&{GXQtWK{N}PFZ`6t1+aHNB4FCWH!XMo& z#8LbLLficVBd1vfS?aEx6lDU7lPUW%x9%;8n3-x@_)Vps?P;)A^W{qaUdqXho>OU` zaT$Q)**oqQ;wT}JiA|hL9zO6{;4MvU4OgN@i=$KpcO;~0$_b&ot8{QOW&heOd&=z; zesjs8>?V6PKPGwkdCgP5Js%Jp)#550&N40oZ2HC54Pfx9fuX>#*j|7H4t%y)3|*s* ztIOnmee1Tpb@iTyUrJlH;l%=1XrP?Tl(}_pdBlu)!)kuX;hcurtKTfLjT1RD?aIo> zUJVV7h+n2u1?gR(Vp001CRexEyqI7&cplnGXkf2RJPKHhA!JK3XBhqv7S%pcAN zg~#3pK$V!5wPM+WFMczT{O>gT`%1%O=GYc~W69y1#@Z|S!)pGt*!h(in{yPDlf?m$ zD^)>;iD+;I<2Jyy9bcbfjFq@kj3W>_RJx(BYs4{i|Deb&b?A&aRzdQl`S-6{zQH*Y zaB`(SbL-yI_G*6d!ChK=g-@x>e9yx#^>Q-B;Cf5S$wePy8MgrdfKqr(E1*;bjr+%I zZ@fCY?8x3*j4`cMFilLqf7SAbewOb@zLb+2Gq>)m2#=Yw$~v4ep{e-L?xwnH1qlF>G&rMsCSnbM=%@vhD>EMHo70GFCSl8cI1sZ#)M|8 zV49egwep^aU&OzkO2q*1K1nx65|NG#WchKO25D{*WFcLKCz_aa83hbg7vtDEFo31dMp#j6so>; zPwqQCtmX@RW6{A~+R+RD&a9wJt{VE7$^@6KOmY^73;+PS(9S=3!@!VH-#w(jl>s4Y zLb)-U)Y>cgi%Sk=H`%rXcTBJ&-(H+=JA7xt?9}|CPhV@Pzg}p+*|*lG3es^hh1DPx z>opnsMBhl_umQGjuP_6`>+TTa2tDn!YJl|)e3r>oYJYV^>v-bUUj1fK>1TUvTLPMp zx^C6I-}`yK^|hQ_sb9D4t?xk6?jXFUe_x6 z-T{XV006qMuD<}{ZXxSxt&S9Ue(;1-PNs+siAvUv7RQ>J%D*ozKD4`u34EhfFc&{B zbM=4;IGMtr+4gpYy}~d2^tHC})cgS=W7E5>$1O|-U01)Z&34JU$8pL4+qYMk86&T` zT8ty;d$iL{hMqR7AphvZCEbpsQFry^qN0Plw2TSXCZO^2GBZ{@`pf*xt^3MDqh<{+ z{7-(>-Z0LZKfv_(pT0C{ zoHoFwO&{`vk)OI!jH6Ut_#)i8AK>R7GABA|iNmLz)m=TgsQA!sEfe^Lk**PVOI_iC zUv)TI^8q0F?hQ96mBm`6Ra<|fIMLB`+=0^u0DvtoAN!C2CtW4RQCey*%rfY^uUiHA z&tLOgFCoO?5w^N(1q+KleXZ5DB>=u%zJ1`;&c<714%K|%&aB_;#g7cxbJ)>ztw!I| zBKoEfV1fn!07}>o0k~{-%@KHuQFZg|;UN<$!4dHbqLY?6b@Xlhjp7AG2X|?$O(5`= z+QI`nJDaM?o!a(2clqYCa%F(kA{C~qEX@sZCM|a`VFPU0ar_Dc`-3aQIKz#KL-Tlx z@rG4Uz|sd^Fmp16Qxh%q*9#YZ+c9irOlT}P@M@Q{d;a}{rZuN5eS8M2HsJJjHC=AJ zCEi95TU@ehb@R%&0)gXxLdEd!gj~H}BlzY?|)CW&74YK3;Y1@pVb}6GCFgzS^}gftPi)-0Vz9)eu5R zz@1Q{h)G^fx>|4F(sk5%w8NFU;;F*9%Qr3r$l&as#ngZ0?;q@lH5>I4I+w`ROt=p) zBr(RG0U*jcH*L+i)GK8lTm>*VA3!J$rT@+xZVDQCJ6w^bY;5ZMO$+=K6LtBCxgOfCJclTLW5?etWt2C zyh2-Zez2e5YUIiQ-F=VzSIMje4=)3V^~xmE#e#RwRbBYftM&s7;Fcd_f41f_A!MFA z(jE&=uGD94*?Zb{sTKfBd*jvOvZMRrd)jN`U72T8{DRBNA9_je9~fa<$n4g+qbJM1 z{LLD>5xoGnoL#eVebO0#{JGS3q#| z`R=wFxyhglnn+$@(J42UZ}{n@w3SaMD3k#wS^D3sRaOl+-%KG z5b~XD9g16ZHg*k7&dsO#uxz9DqC)hK(r8vRWMogV=B1%&CbA^^7gJz`d^ghk1Oax-0#CT}s`sHx0vtSrq^OdfF!C>+%1>f^c+3C* zu=SN=e`8>O^<hmWwNiu+V^qoJxhF9JKtl6KP^&%nOb3Wlz!|M$I0Q++^KOrE$6I;hrWKtLa z0CKal9s}?`$VhMQmT-niV*mhnZ%^hj!sro#VK0Q1aFR)70078+eSIuA;W!AHJ@(ZS zV=?Ir005u7oe|P+lpQ7@E5+6_+40J8fU$V&h5RmAQfLNacvEaGlbuP~0$8&@XZ`m8 zmP>$1vm3{onDhn!fV}LCSr#HcN`OhRAJ3bZl(zs|HocsG)54L3VC)02wM;gqLhW7JOf644Uj2u2GCeY2CzfkTlcDXllle5u!9gx=r~z# zM>4=D9N4`s+(79o0DGPcIsSd>AQ@mBLO;Kh& z!U+4%sj?hRGJuoh2GjO`-WMNW? z^>*@dG;uu0Qw-Qh0Eb^OD+ZDQ+(d46MgoIu8-PuO5U1Wf=m{hPc!2kJubVAU`X~W= z5Fi<1>#;^MfQQN3nV}X`vh|FS4Y*?nQAZpl1BlH2-RtL2BCHmG)dXZU2t4}YP00XW z#(UYCBnq;c5w?f`^8n0sXDcBYU=na}&x*iaDCZK8xrC8fj3JQ#aSVtfgiPoih>`)O z0ROiqV#JjBWNa z^nbyL+Q)W%k8S|C`^=3ENmREbSHqCedj9(l1Vgt51U_oD1`shb9Ck!Zs6(i2Cw=0x z>zHec1wHe1M#JqJw0AP`{|^DQhQ?;c*(N?I&kS+1NGV_=RcFZ!&%P%X)k;>1B1a_8 z0`;5_OI=5xD$0QaIKVSUXzzS3%!C(Aic&PU>=FBwJOq!-gg4-VVgm{$r=5Aouzr7D z)x59@7#;}sM#g6NsX<6WyH>yd&q`D^plG6niOrw>+pH=NH6PS?ZZGcKZl$dPIxtx8 zp4GLjQ{g{;kH(wTuCi=*{8~hpCr5I#xaR-K{g~SOvSPV05aiudOaYTfiMq5>p7?Wi z0z!+a^FfTEzBX~qrS-Y^O>K$}BLyXs%ihr%cw6)m`+*;O*Z9Su$TK>6NR%uOD0#(j zm(+A6Xv!&~`I$uOu=xno_&*eCb6!gW#F^TE{uk=;hcOY{89lW?Ls>r35aeY}+v)tSSi zDp9zV6A&YI&!Js6Tt{|^gwJL#q|jM`?6138LJBI^hsl4VE2MXtoHz=Yo=$4^X zRBt0-@;8$JeVOv*84vij{TU8*1gL!Yq+gl3_+nv@<83`kJm+>DOrkmG&+2wt z9ZO?M4h;ntwaJKWt?6N3F_PonAEQk7IUnLZ30Af_5f)#0GZBPoRS(>5WaBjr-qvM67t)5Y!H_k`ko~j%r2F;uabKsot(i9cRc$xYOZ*$S#wJwB-Xr`H_J`h+b7{#x_S3E9GcG0H-z?C z4O>|N)=NTyezbXgXp1Ot#3W9!-g2_Jyxa{}5mEIJKj`=r zQJb&t^Z-9l09G(ncb>K2m$(pwOMh~6!f-|vUw{8-iV#@kPZw$-c!2o+s{`?64P?)` zw+zngjX-r_Z@#P(+|Xvn!%0#NHzheP<@D3=Xhp4EUpclt-~!qU6{^ zmDSKUyE`-TuNvm-^5A&rc-qHC1hkT`^$wf`3FBZSGTH z2Tq$HJ#axep)`9)$Dd6kZJ`e$#CK~<*yrVd;V-r1_a(+ZG z`xCJ=Xf=jn&e77PZ*i9BV(z{%zMht7C;d>*c*JWe7^Bz3^KVj79bG8S&-Ms->g9oA zM9V}nCDX|!W|rbE;;j=RRg?R&jzRqTXhK1FoXLFpvD=BFnMOBzHz`TJI8aH`iam%Z zNUFVh}L(=!L9``^1&1y;FIo@4b4W@6YBnskkuDC5O?ya0DE9tNizw5~-Ic z<`1BBB1b4LJl*?J5NumR`^5Naka9LoOmC|5ar-|9j+$!uouLDQs>HaykI?M0k;raa zLeH(yqj5@Nw#3Q`6mZcDU~Z0W4=8XFUyHtO-uv~bMMz^7>b|vO4+rXUyT!d74^e_f zvK4$D-r1S27z`Od?%7wBqyyl*lVAmsgVVC>QQl0iM6uot=}c=BNW+m2tA$-JuCaHq zd&9GXWBkoqnk4T+AAfWNtbW62vwu_^Ck@}C-nWeAYg$(X8j4TKW&oJ9!jkLv@N1rMQNsta_9uU6Rtn4OREhPwmXzWiKjy&% zIGT~hFOfk}%<1YKHkImWM4mk5m_!&lXWS;-5XUGJG(8^ZD0(usDpM-YWWk1WnQXq_ua!W{8F zaEDGw{yZQM$chVoMkJl;HHaJhLfX1;lr~YFz$f0hedl95inCk&U3_PG+)>N%;o5k2 zX7_jMiaqL*xo;CixbW$+U|*g#P?CB6%o^!(UwTr{sJCBf3b0yNdTzCOFWlsO`pQIQ znisM<;qJ^q`r1saHi87jAG-wmm08ZG2>^!MG0--%T-#5Q2gdJA?XL;0-h^ZC8H#hi z;MDf=77+Dn-QRo92<>=dmJQ24m%BGL=b@Zj5sxnCvLYP>eA~GW)(Lha(+i7FQM3W} zmH4@d%`Mk{wU?Dd>NC^F-X^Cuoa_s&oLgkQ7A<{5x`lYABi$~uYDS85>FEuZai$-n zi#$?A?EIrRsuDG?l75~nJAU;;mL(kp{w8BF9m@AgI9xgos#D92`l8-HW|}pa!DV-; zkA4C(j>wKsrsMDQ%q7XCv;#UR@ctcWxhgSarH0d>=aI`V1rL?fd;sy$C>KIfp+lGz zArr?nUpY4$cVRu@6wAx2l8~2XVz^m#L&?z1A%RFSe9Z=;aF7^aQ^p@Y;|nPTWOy$t zYt+npT73;Xkzm(ciyy~0-l=}xld^L9i?adDFu0LXxM^aOotggu_zb`v+OP8eSe+X% zx&IhB6DX==}xyf`JXwN8;l{g=veZO+)5IABx;D(ILFfA+fJT$g)6IPZndF*r& zvi1B)In7Kqp~Ujm9Jqsm&(iYu?>e{KwQ#A6(tcllnI35!>i5uy;Mi1M9S={JXB7qs zM4gT@lMuh4B=f2D&+|vb_5c|ei%atuv>+0@+n%U=+v!wKWN!#I;1cqJ_;;(Sf4dyt z_zXLNA{t;t$G^-JyX_=+Gy9#A%9yE(_24;1y8&wbdak0T>SEL8Zyg@M$3d%EZDRc~^-|#(*4IFs> zY@c{|l zw|X8juT+J&TZ>0GwQiGtVwUX-P5D1ugOLH&lZ4Frs~b+ZYpdq#1+%to?MnBAiFcRf z)YpR2GO}l94Y)HyIy>qFK9Z_yE;)gBH_D5b@W5N%(WwRxE@{~f2H}93;A6ZK4%Fn8 zU(w-$at_+x#d85C;-|WNB;hRbgymf%gfH

4g^*)9C-pxIbb!KOQYyjn${_e>lM0 LglPQ4h)nwr?<^A{ literal 0 HcmV?d00001 diff --git a/renard-wx/images/goods-share.png b/renard-wx/images/goods-share.png new file mode 100644 index 0000000000000000000000000000000000000000..d0b018bbc3cf838fa72fbd0042784752102c64f7 GIT binary patch literal 2354 zcmb7G`9BnT7yix|V}`Mgu@x6x%UCj$YaPZkk_ow!B}-$=7KtQGG-GZtq2k(=^)34h zZe*vBY*`|?SC$AxOesl5uHL`keSbLTJm>kG=W~90&beuCXC*8sCkOx#wmw0k>{rIm zK*0BNmzmc90ElgCl7&MQbuF(g0xpmMA@9+xq{S+#6nN3+ce{> z(=3cN35ma^pQupcH}Ff8EY~uR3s7v!kY<(X<+-aCUtzYPeJRE>!|O8}mra;Le67=Y z1sDD4Ay-7>_U=6b#%#Q%D2lNlh6DRqD%3sp_WuWrpmWjePiGRt6MDEU)9X3Hqsh>Z zRo}bh=AXvG&IsIZ`Azan)B))1W;5I8tWke`kGOK?IXnqRWU8sI+)5ijfW?0Xv|-hP zh}AG*sf|eHF16Ir@g**WDs}#-J`I!nDe0cgi7(Ojciw4al?c50K{Bi^|V~}xC$P8zS6#8M(KanSl5fkv&Kp>9L563?LUcfZmAq<+Jy$4?5LnT9l+?$@o+;u{_9|_;Nhc z0SpT55w-9*UAWZKIMpv74PLe;`M|#M{1X225F=-ft2MPj5+-RGc30P?_0i;T`G-;| zmD(GgG9qy~c<2T2Hn?LpaN8}L=RX5o%$mKZJUUgAYx_YQ2bN;}T6?s%5Het4y}zWB z%>JAIx^&5Y&j{vaShHPz&g3*|w2%ijdusdeP`ILrhcD&f`=1DH;OkcYU(U_L*Qura zCwD};+|ks!@-t(dLW;*o97NMRAS0)YufkA_+=Arv=D{-B+YVQ?9INtFiWN}WYU^Iw z-*DVLr%J*oZby_GEKWq;B)j;X78mQKGrm}Lv~8`KB4rup+D~8WFQ{L5;X}BHhWwVH zNzstGextUrtZn^KiqL5c%qhSAA%0)HpTe3WYQ=u0?PZzEGLk{r_sgXCdx% zj-!b&kt5SPd1`U8hI=mczZ1I+SSjjC$BpFyywckgm-_sW`Lo;)s=3$o z02Z9Mq^uG2Y{$!7!&Yk%s{&V40@v;wS|QneVa*_2Ik_U6NATwJXY%j)R3(OCZM(gI z1Q1J0Q;T#rht6N2b6<7AUSiGXb@an{N+=?>tHRLZ6!5e9|2qcO!hplOjs?W-pq;tqq0`Q z6z7WiB{VG_8YuL6vxh-K<&)|t*@E1BpGHDBIWAp=F(J64E{8leLq#P@~09Z z)EoIdKf%i2m!dI@E_IleFx=x>0AbZU{vzKtb|HLjj6XjZ5_^W%6$b{tnw4@k+EO*uT6Focozb~;bfU3a8W434u_ zrf3I}q~q~e7BEd>dfZ6%8{9YI13NDH^Us+=aKdOsQQ$;;NBaYH*9vbGK_fht*64mQ z-*N+$$jNVT+vGv==%g z??UWho)jA!gG)2CA~r7!bufd8KCkIQ!2)1 zf6zlCuoI}~unlj>Fy_BetkG~nU+`0GH;{amMb6!~3_ndfC3vA>yTgm?5PLnhFhO1L z^wWbsRZ)yT|4uy3f~eot$vE1l0tlh6SL+ls0=sWeTogZ0kBziiA6@J#ENMOIF;i~- zxj}iTqb8y>L?a3F?#)1TT7kQ>wE-J155lXRTkz#C8!~Q2zhB$;?-8T$L9ai19xyd+YtbCTwJO#pGf@G*n(TuCx4w~^CPqWCzWc|e z_%Z2%vJ7OTH(A!5&HswyaSk7Z+?}ZwA=Ee7DWHytx;L-rB+>5jSQ!*pN^^f0k_AV_ zSq=|0Nj|wbJy-{_vtySySDjyw9=>EXdr98pLGP+Vi7dSo7 z>*$eT@V2`vNGOJ5_;R%_T#;^8t0B(k`SSI4P%@;q;8Yd48=TRP{2}M_zsf$)10qAG U>I1WG?*Br-+R~21A$l_Z1x3FsDgXcg literal 0 HcmV?d00001 diff --git a/renard-wx/images/my-icon-1.png b/renard-wx/images/my-icon-1.png new file mode 100644 index 0000000000000000000000000000000000000000..8bd903526c7a6f459f4e3635720af25a64e7959e GIT binary patch literal 1214 zcmV;v1VQ_WP)O15sLEfGq)Xd>1q<&r=o7y^Ny4;U{o_+WhU#fV0Xktd^x zPb3&0e8BiXOyogBNF+*-#FTI;sIZk1?3UhuWhve5Zg=;b@nKo$EGx^T_jy24 zXqtzb1y8YX=^BL8J_B}70lwWdKccUu$KyYqI=q*@bH9^L#!;%Mfta&|(D3>LG_|i~ z`AhF*y;Xr1fm2Q+YT8!r+p|Z$<6`$g%*HmF+IHZntSSp=c4C~q^S^Pi`yjer>FL<; z{ywSc%Yn>v(1z65wLpK*G5pmFcwys*)CL!q1+t)EO9w~x@1(!y7~#&>!~U9J5QqZE z$qloaB&lQ!e|50zkN~y8#rUg(q>?eRndFo@IkUM!2nTbSl?9O5*&{`A%LEv()C71@ad?*3E?DK-2x4aL(<(wydK zm0^NmmZamk>XBLXk3mZ<*K;(JPVn!EpGb~fMcn7MPoeM|^(^VwMum5tD<9XOW7qpR z*R`K)I_Ww*%_gN39e|OOsF0F| zmX4ypN})$)F+Bz?wOr3pWm^ngI+0&Y4jXHw6jIX&HZ8~Z0DiNsL9Y+@aO}WtG840h zCo7lX=~Zv>?7DYL23U}5(D`*uggaj&dGo6CQAH_)lq_7j#?|wbvK_3mhdtMzo=QLA z&NtjPca&}C7`X5!v4MY>NR7?}6O!cUFhXd>gWj*KT!xds?xFYe&)8NDsX2EowF;!v zXB_X!2HGF_gWm36@OTYs>YLF#-YNA$2!zy#4V)*Hh=H1lS~k$3OUJRT9JLKgsHtzc zlPXeqNU2v48;p=n#xXByz?)wzvg8iS9Z`l3un?N6#`B35F3mz679weEo0@@U6+kOXd`fAvUeFlr)e@| z-xF-u$6}3ItN~5F0qhNc0S5D9p+&yT*+`XfqIT@^OgVVwPZB zB1l0Va_~GO(X*KOIhip6p;v&e+5Dz}dMf<*{B!+&PY~BGJx|{!$Oo`7h?I&AT$(|Dj=sNf%O>OH+mdH)s7(!|~v7sIk@j=YS zFe^6g#0Z2w0Dica|Mb-O>hyU0`%{PaHkCRxuIpZe)UfR=!RF`a-0}%VAoKoX0DwH z2dD7%yu!fgcd*sDYXX^*v*9qeMlUcsaYb}=4cd!&BtR^(s2LkETD>Q04hhiKIe^eK zVynTD9togOZiPZft!8kA5+c)XLG*b*_YK6_OrQs~b-T5hF&1ba^dYoX?8m(bT6Dnf zMbM(!P6DzmnWj;35HU?7`$?gcd0GfL1zZD?IgjO_8}SIg{`4t#{Z(2_bggT-Cx8D5TW!v>#_Zx=hodCs8WBqkb^QgOH7(mV;`#onw8kGxps@7U#yZ6KCsyFN8p|)qzr&W*SeM53g_NV_>o>pdjda zb{L2I0HMFfFj5-deB!H`_1-;Hi=$~;?8`0X(6)5=UbyPZytGTVDM+fNW8jR0Q zTyf6-{v)7vWg}O!*?H>3t0jH8!-nU>f!iZDN6$C!($+Y>em}lb{x5|PZvgrAN^Uq5 zo{F4|-&^`7Fn#s0T{vvO)82C#@KdnqPzn^_K=!>GnkRFNvq_a~VC*UV2S)8(FW)=8 QC;$Ke07*qoM6N<$f>s*=(*OVf literal 0 HcmV?d00001 diff --git a/renard-wx/images/my-icon-3.png b/renard-wx/images/my-icon-3.png new file mode 100644 index 0000000000000000000000000000000000000000..3f678b5097013ceabc273ce727183548f5e04bff GIT binary patch literal 1098 zcmV-Q1hxB#P)}Nz*2|_xg}#P12j*bWPg@`h^SOoZtDK z&-vYx-~HWlp+Y3GKQv7%Ybc7nrjH5)4Wc~%)K<(Y#h-Fr3+eWL``OE$rF`o zTWsznc~l6QSa^copT9J8nz%cSB%2NGB-zZ3-@e0E<1w_2g(na~E)6sh3vuJpNn-$$ z1&g)11W*&B{by0rNkiKzdtGUunwF;4?PG8ICx%WV)1&kiEGV0xb91VaPO|4f2Tsq! zhCcf)og^9>D+N@=YOiDGV=o&zoeK1E{X%yRq(o{ylUB@CjzyoU!nUS;Y-wCRj{@Ah z`V*1qe~m%2u%$>zXLF+fArYGi5E>r9p@8B{}4 z6;w@OThm_l9C!!U!lT#I;OFmOPcVL~7fn@IZE0HwTDy|Lvc*QVs{#MhhiQ81wY;S6 z@;0;o_0Jf-{3C;Zp2lRcl@)YFyUFuV3+;w5^434ls$@`6^2^;? zC^PrcvN#XaY;j<7xQR!C%mjzmi|w9fg2TikL2M2;W=nnzn_DPNnPgY%A#63CV?Z?9uXi+l{K}cGcS|`MFQ*EQ#y_{(U7u#! zg)S&$J~n)Fd+>b627GMLcC@s8C?xsV;sT0;LT2we%Se&}V)rvJ3tBPyANZ1jD_MNZ Q4gdfE07*qoM6N<$g2w?DhX4Qo literal 0 HcmV?d00001 diff --git a/renard-wx/images/my-icon-4.png b/renard-wx/images/my-icon-4.png new file mode 100644 index 0000000000000000000000000000000000000000..5b8a5fd1b1e13c2e501d3f66d9cab07f648fad7b GIT binary patch literal 1735 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 literal 0 HcmV?d00001 diff --git a/renard-wx/images/tabbar/cart-o.png b/renard-wx/images/tabbar/cart-o.png new file mode 100644 index 0000000000000000000000000000000000000000..02df8f13e2c387c7497d75199ec521e31092a2d9 GIT binary patch literal 3418 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 literal 0 HcmV?d00001 diff --git a/renard-wx/images/tabbar/like-f.png b/renard-wx/images/tabbar/like-f.png new file mode 100644 index 0000000000000000000000000000000000000000..6b28364c81425d3c08a39cbfc03516ffd40e430b GIT binary patch literal 2115 zcmZWrdpy&B7yoXo#^yF{$fd_6`o$2+3^SK9k`$4Mxt80khNQ_{=9Wxu$s2>Nk3BA(4ttw+TuCsmmxq66C6hGIfb(1g$kCSk&l^07fl)_uB%j$?;kA#yKOu;_Z3W8d+RH-Ox@+aaQ>` z8iWkvsKer1H&`e>t#+VDuim=TR_&*@ikWYJM_6z#oo)8 zY-hN=y+81eF8O51tOu7^2zxlkbM!t#iJnYW(#E4^^QJA!YWel!v*qv92yz!^KV~bCN)SS0?;>`NN!sqX0 zCbCZ^{=JDT9nv>rpxjxEq-{!g*s69;b9K9RqE&v-o)YuN9qrH7w=d7?%n7KVX}_0K zu=p5oWW&ssY}z^nGL>p|JX}76wokmQ)7t0KF5OSEl}1D8Ddzs3%q{6V^(zfFP$M@T zvMq+Mv*d7rd!a2mz6`U;{(a!bW`$-ZkT=EIR8tDO58fcK109?i4V^na|- zr8xq<{?d}v*+Vc6(E!F5K3sd`8iL#v_RS){PWcg%ic$8BCg?@tg@zY1;#ID`mYC!? zJ(>U9Te`KI5J@{3ZwWWbUhp#E2P}Mp)o5ir1jlkQ9mq!>^QSP`ZT=fl2$0*zzYWX` zZg}+=0@t_+w-d!0@S{AUF{8E(d3{df_|?| z?Blm+ZOpp_c_-D{D1ZW0-yht0AGZ^5+Y3L|hNJTinwZ9_Es$jWdhrfA zqPu38y+GHxgoM>e(;CpbMlRF^qsHvh;YpxOEb5#K&COt>M2gobEjrgOoH>ybIYs0J0k4)mE zJ9;`@>6!NuaP!i_?85kZ{U0DWkNf&~RN%0VGDn8$95s>mi+stdFiZQ zZr_WcEmSkP@e92^%8Hj^Qsc|0W6=UFcmu|59>v^^VT>Bu#fb7oZ0wC0G<7Ii>1Zm* z8z*6_FEewWqz?bO?w}Z#Kszkzll~@(1CpUV(XI64HY%d`qxz?hobFR1bb!QcF{k_8 zpNJ-dF(Sk$TRcjs{ue?fuIUr>7yWPw(N22jNCx=3Ji z^KxYrVLVtEiRCEA_r_d$? zeIiDtApEuFVMAjPKm`H}PTBOpMPevOAZ4msYPFF{Pl}8)uV%*{+K{m8&0k@bx%Yz? zb(Ys1iYPFbISMTPk1nzl7}`RQ&T93)LSR}0WK?3^NtZQNf%rbAJwOx-rQM#Gv4|5U rPcXO*WlC3Dn*MJ$i?+3+)way1RnS%2{2yYX&jH|Y)Y*<{LrnTF!{^<` literal 0 HcmV?d00001 diff --git a/renard-wx/images/tabbar/like-o.png b/renard-wx/images/tabbar/like-o.png new file mode 100644 index 0000000000000000000000000000000000000000..bb0cec58e1d745018c139d6f5e42ab0b2ac9e529 GIT binary patch literal 2832 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 iR%n+4K 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 qkw_#Gi9{liNF)-8L?V$mZ2k|sCcD;y78ua`Tx0 literal 0 HcmV?d00001 diff --git a/renard-wx/images/tabbar/menu-o.png b/renard-wx/images/tabbar/menu-o.png new file mode 100644 index 0000000000000000000000000000000000000000..fc42f7cf4561a354d2c22807f0cf51ba689837e1 GIT binary patch literal 3974 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 literal 0 HcmV?d00001 diff --git a/renard-wx/images/tabbar/user-f.png b/renard-wx/images/tabbar/user-f.png new file mode 100644 index 0000000000000000000000000000000000000000..c734a2af0ae00b7baf2fa2fa0f2503b0b3f4968b GIT binary patch literal 4770 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 TPL1sSrDkqoWn6X4J^FtD!38ig literal 0 HcmV?d00001 diff --git a/renard-wx/lib/wxParse/html2json.js b/renard-wx/lib/wxParse/html2json.js new file mode 100644 index 00000000..e0df8892 --- /dev/null +++ b/renard-wx/lib/wxParse/html2json.js @@ -0,0 +1,242 @@ +/** + * author: Di (微信小程序开发工程师) + * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) + * 垂直微信小程序开发交流社区 + * + * github地址: https://github.com/icindy/wxParse + * + * for: 微信小程序富文本解析 + * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 + */ + +var __placeImgeUrlHttps = "https"; +var __emojisReg = ''; +var __emojisBaseSrc = ''; +var __emojis = {}; +var wxDiscode = require('wxDiscode.js'); +var HTMLParser = require('htmlparser.js'); +// Empty Elements - HTML 5 +var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr"); +// Block Elements - HTML 5 +var block = makeMap("br,a,code,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video"); + +// Inline Elements - HTML 5 +var inline = makeMap("abbr,acronym,applet,b,basefont,bdo,big,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var"); + +// Elements that you can, intentionally, leave open +// (and which close themselves) +var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr"); + +// Attributes that have their values filled in disabled="disabled" +var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"); + +// Special Elements (can contain anything) +var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block"); +function makeMap(str) { + var obj = {}, items = str.split(","); + for (var i = 0; i < items.length; i++) + obj[items[i]] = true; + return obj; +} + +function q(v) { + return '"' + v + '"'; +} + +function removeDOCTYPE(html) { + return html + .replace(/<\?xml.*\?>\n/, '') + .replace(/\n/, '') + .replace(/\n/, ''); +} + + +function html2json(html, bindName) { + //处理字符串 + html = removeDOCTYPE(html); + html = wxDiscode.strDiscode(html); + //生成node节点 + var bufArray = []; + var results = { + node: bindName, + nodes: [], + images:[], + imageUrls:[] + }; + HTMLParser(html, { + start: function (tag, attrs, unary) { + //debug(tag, attrs, unary); + // node for this element + var node = { + node: 'element', + tag: tag, + }; + + if (block[tag]) { + node.tagType = "block"; + } else if (inline[tag]) { + node.tagType = "inline"; + } else if (closeSelf[tag]) { + node.tagType = "closeSelf"; + } + + if (attrs.length !== 0) { + node.attr = attrs.reduce(function (pre, attr) { + var name = attr.name; + var value = attr.value; + if (name == 'class') { + // console.dir(value); + // value = value.join("") + node.classStr = value; + } + // has multi attibutes + // make it array of attribute + if (name == 'style') { + // console.dir(value); + // value = value.join("") + node.styleStr = value; + } + if (value.match(/ /)) { + value = value.split(' '); + } + + + // if attr already exists + // merge it + if (pre[name]) { + if (Array.isArray(pre[name])) { + // already array, push to last + pre[name].push(value); + } else { + // single value, make it array + pre[name] = [pre[name], value]; + } + } else { + // not exist, put it + pre[name] = value; + } + + return pre; + }, {}); + } + + //对img添加额外数据 + if (node.tag === 'img') { + node.imgIndex = results.images.length; + var imgUrl = node.attr.src; + imgUrl = wxDiscode.urlToHttpUrl(imgUrl, __placeImgeUrlHttps); + node.attr.src = imgUrl; + node.from = bindName; + results.images.push(node); + results.imageUrls.push(imgUrl); + } + + if (unary) { + // if this tag dosen't have end tag + // like + // add to parents + var parent = bufArray[0] || results; + if (parent.nodes === undefined) { + parent.nodes = []; + } + parent.nodes.push(node); + } else { + bufArray.unshift(node); + } + }, + end: function (tag) { + //debug(tag); + // merge into parent tag + var node = bufArray.shift(); + if (node.tag !== tag) console.error('invalid state: mismatch end tag'); + + if (bufArray.length === 0) { + results.nodes.push(node); + } else { + var parent = bufArray[0]; + if (parent.nodes === undefined) { + parent.nodes = []; + } + parent.nodes.push(node); + } + }, + chars: function (text) { + //debug(text); + var node = { + node: 'text', + text: text, + textArray:transEmojiStr(text) + }; + + if (bufArray.length === 0) { + results.nodes.push(node); + } else { + var parent = bufArray[0]; + if (parent.nodes === undefined) { + parent.nodes = []; + } + parent.nodes.push(node); + } + }, + comment: function (text) { + //debug(text); + var node = { + node: 'comment', + text: text, + }; + var parent = bufArray[0]; + if (parent.nodes === undefined) { + parent.nodes = []; + } + parent.nodes.push(node); + }, + }); + return results; +}; + +function transEmojiStr(str){ + // var eReg = new RegExp("["+__reg+' '+"]"); +// str = str.replace(/\[([^\[\]]+)\]/g,':$1:') + + var emojiObjs = []; + //如果正则表达式为空 + if(__emojisReg.length == 0 || !__emojis){ + var emojiObj = {} + emojiObj.node = "text"; + emojiObj.text = str; + array = [emojiObj]; + return array; + } + //这个地方需要调整 + str = str.replace(/\[([^\[\]]+)\]/g,':$1:') + var eReg = new RegExp("[:]"); + var array = str.split(eReg); + for(var i = 0; i < array.length; i++){ + var ele = array[i]; + var emojiObj = {}; + if(__emojis[ele]){ + emojiObj.node = "element"; + emojiObj.tag = "emoji"; + emojiObj.text = __emojis[ele]; + emojiObj.baseSrc= __emojisBaseSrc; + }else{ + emojiObj.node = "text"; + emojiObj.text = ele; + } + emojiObjs.push(emojiObj); + } + + return emojiObjs; +} + +function emojisInit(reg='',baseSrc="/wxParse/emojis/",emojis){ + __emojisReg = reg; + __emojisBaseSrc=baseSrc; + __emojis=emojis; +} + +module.exports = { + html2json: html2json, + emojisInit:emojisInit +}; + diff --git a/renard-wx/lib/wxParse/htmlparser.js b/renard-wx/lib/wxParse/htmlparser.js new file mode 100644 index 00000000..7406b45e --- /dev/null +++ b/renard-wx/lib/wxParse/htmlparser.js @@ -0,0 +1,182 @@ +/** + * author: Di (微信小程序开发工程师) + * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) + * 垂直微信小程序开发交流社区 + * + * github地址: https://github.com/icindy/wxParse + * + * for: 微信小程序富文本解析 + * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 + */ +// Regular Expressions for parsing tags and attributes +var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, + endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/, + attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; + +// Empty Elements - HTML 5 +var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr"); + +// Block Elements - HTML 5 +var block = makeMap("a,address,code,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video"); + +// Inline Elements - HTML 5 +var inline = makeMap("abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var"); + +// Elements that you can, intentionally, leave open +// (and which close themselves) +var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr"); + +// Attributes that have their values filled in disabled="disabled" +var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"); + +// Special Elements (can contain anything) +var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block"); + +function HTMLParser(html, handler) { + var index, chars, match, stack = [], last = html; + stack.last = function () { + return this[this.length - 1]; + }; + + while (html) { + chars = true; + + // Make sure we're not in a script or style element + if (!stack.last() || !special[stack.last()]) { + + // Comment + if (html.indexOf(""); + + if (index >= 0) { + if (handler.comment) + handler.comment(html.substring(4, index)); + html = html.substring(index + 3); + chars = false; + } + + // end tag + } else if (html.indexOf("]*>"), function (all, text) { + text = text.replace(/|/g, "$1$2"); + if (handler.chars) + handler.chars(text); + + return ""; + }); + + + parseEndTag("", stack.last()); + } + + if (html == last) + throw "Parse Error: " + html; + last = html; + } + + // Clean up any remaining tags + parseEndTag(); + + function parseStartTag(tag, tagName, rest, unary) { + tagName = tagName.toLowerCase(); + + if (block[tagName]) { + while (stack.last() && inline[stack.last()]) { + parseEndTag("", stack.last()); + } + } + + if (closeSelf[tagName] && stack.last() == tagName) { + parseEndTag("", tagName); + } + + unary = empty[tagName] || !!unary; + + if (!unary) + stack.push(tagName); + + if (handler.start) { + var attrs = []; + + rest.replace(attr, function (match, name) { + var value = arguments[2] ? arguments[2] : + arguments[3] ? arguments[3] : + arguments[4] ? arguments[4] : + fillAttrs[name] ? name : ""; + + attrs.push({ + name: name, + value: value, + escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //" + }); + }); + + if (handler.start) { + handler.start(tagName, attrs, unary); + } + + } + } + + function parseEndTag(tag, tagName) { + // If no tag name is provided, clean shop + if (!tagName) + var pos = 0; + + // Find the closest opened tag of the same type + else + for (var pos = stack.length - 1; pos >= 0; pos--) + if (stack[pos] == tagName) + break; + + if (pos >= 0) { + // Close all the open elements, up the stack + for (var i = stack.length - 1; i >= pos; i--) + if (handler.end) + handler.end(stack[i]); + + // Remove the open elements from the stack + stack.length = pos; + } + } +}; + +function makeMap(str) { + var obj = {}, items = str.split(","); + for (var i = 0; i < items.length; i++) + obj[items[i]] = true; + return obj; +} + +module.exports = HTMLParser; diff --git a/renard-wx/lib/wxParse/showdown.js b/renard-wx/lib/wxParse/showdown.js new file mode 100644 index 00000000..a1f830fb --- /dev/null +++ b/renard-wx/lib/wxParse/showdown.js @@ -0,0 +1,2529 @@ +/** + * author: Di (微信小程序开发工程师) + * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) + * 垂直微信小程序开发交流社区 + * + * github地址: https://github.com/icindy/wxParse + * + * for: 微信小程序富文本解析 + * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 + */ + +function getDefaultOpts(simple) { + 'use strict'; + + var defaultOptions = { + omitExtraWLInCodeBlocks: { + defaultValue: false, + describe: 'Omit the default extra whiteline added to code blocks', + type: 'boolean' + }, + noHeaderId: { + defaultValue: false, + describe: 'Turn on/off generated header id', + type: 'boolean' + }, + prefixHeaderId: { + defaultValue: false, + describe: 'Specify a prefix to generated header ids', + type: 'string' + }, + headerLevelStart: { + defaultValue: false, + describe: 'The header blocks level start', + type: 'integer' + }, + parseImgDimensions: { + defaultValue: false, + describe: 'Turn on/off image dimension parsing', + type: 'boolean' + }, + simplifiedAutoLink: { + defaultValue: false, + describe: 'Turn on/off GFM autolink style', + type: 'boolean' + }, + literalMidWordUnderscores: { + defaultValue: false, + describe: 'Parse midword underscores as literal underscores', + type: 'boolean' + }, + strikethrough: { + defaultValue: false, + describe: 'Turn on/off strikethrough support', + type: 'boolean' + }, + tables: { + defaultValue: false, + describe: 'Turn on/off tables support', + type: 'boolean' + }, + tablesHeaderId: { + defaultValue: false, + describe: 'Add an id to table headers', + type: 'boolean' + }, + ghCodeBlocks: { + defaultValue: true, + describe: 'Turn on/off GFM fenced code blocks support', + type: 'boolean' + }, + tasklists: { + defaultValue: false, + describe: 'Turn on/off GFM tasklist support', + type: 'boolean' + }, + smoothLivePreview: { + defaultValue: false, + describe: 'Prevents weird effects in live previews due to incomplete input', + type: 'boolean' + }, + smartIndentationFix: { + defaultValue: false, + description: 'Tries to smartly fix identation in es6 strings', + type: 'boolean' + } + }; + if (simple === false) { + return JSON.parse(JSON.stringify(defaultOptions)); + } + var ret = {}; + for (var opt in defaultOptions) { + if (defaultOptions.hasOwnProperty(opt)) { + ret[opt] = defaultOptions[opt].defaultValue; + } + } + return ret; +} + +/** + * Created by Tivie on 06-01-2015. + */ + +// Private properties +var showdown = {}, + parsers = {}, + extensions = {}, + globalOptions = getDefaultOpts(true), + flavor = { + github: { + omitExtraWLInCodeBlocks: true, + prefixHeaderId: 'user-content-', + simplifiedAutoLink: true, + literalMidWordUnderscores: true, + strikethrough: true, + tables: true, + tablesHeaderId: true, + ghCodeBlocks: true, + tasklists: true + }, + vanilla: getDefaultOpts(true) + }; + +/** + * helper namespace + * @type {{}} + */ +showdown.helper = {}; + +/** + * TODO LEGACY SUPPORT CODE + * @type {{}} + */ +showdown.extensions = {}; + +/** + * Set a global option + * @static + * @param {string} key + * @param {*} value + * @returns {showdown} + */ +showdown.setOption = function (key, value) { + 'use strict'; + globalOptions[key] = value; + return this; +}; + +/** + * Get a global option + * @static + * @param {string} key + * @returns {*} + */ +showdown.getOption = function (key) { + 'use strict'; + return globalOptions[key]; +}; + +/** + * Get the global options + * @static + * @returns {{}} + */ +showdown.getOptions = function () { + 'use strict'; + return globalOptions; +}; + +/** + * Reset global options to the default values + * @static + */ +showdown.resetOptions = function () { + 'use strict'; + globalOptions = getDefaultOpts(true); +}; + +/** + * Set the flavor showdown should use as default + * @param {string} name + */ +showdown.setFlavor = function (name) { + 'use strict'; + if (flavor.hasOwnProperty(name)) { + var preset = flavor[name]; + for (var option in preset) { + if (preset.hasOwnProperty(option)) { + globalOptions[option] = preset[option]; + } + } + } +}; + +/** + * Get the default options + * @static + * @param {boolean} [simple=true] + * @returns {{}} + */ +showdown.getDefaultOptions = function (simple) { + 'use strict'; + return getDefaultOpts(simple); +}; + +/** + * Get or set a subParser + * + * subParser(name) - Get a registered subParser + * subParser(name, func) - Register a subParser + * @static + * @param {string} name + * @param {function} [func] + * @returns {*} + */ +showdown.subParser = function (name, func) { + 'use strict'; + if (showdown.helper.isString(name)) { + if (typeof func !== 'undefined') { + parsers[name] = func; + } else { + if (parsers.hasOwnProperty(name)) { + return parsers[name]; + } else { + throw Error('SubParser named ' + name + ' not registered!'); + } + } + } +}; + +/** + * Gets or registers an extension + * @static + * @param {string} name + * @param {object|function=} ext + * @returns {*} + */ +showdown.extension = function (name, ext) { + 'use strict'; + + if (!showdown.helper.isString(name)) { + throw Error('Extension \'name\' must be a string'); + } + + name = showdown.helper.stdExtName(name); + + // Getter + if (showdown.helper.isUndefined(ext)) { + if (!extensions.hasOwnProperty(name)) { + throw Error('Extension named ' + name + ' is not registered!'); + } + return extensions[name]; + + // Setter + } else { + // Expand extension if it's wrapped in a function + if (typeof ext === 'function') { + ext = ext(); + } + + // Ensure extension is an array + if (!showdown.helper.isArray(ext)) { + ext = [ext]; + } + + var validExtension = validate(ext, name); + + if (validExtension.valid) { + extensions[name] = ext; + } else { + throw Error(validExtension.error); + } + } +}; + +/** + * Gets all extensions registered + * @returns {{}} + */ +showdown.getAllExtensions = function () { + 'use strict'; + return extensions; +}; + +/** + * Remove an extension + * @param {string} name + */ +showdown.removeExtension = function (name) { + 'use strict'; + delete extensions[name]; +}; + +/** + * Removes all extensions + */ +showdown.resetExtensions = function () { + 'use strict'; + extensions = {}; +}; + +/** + * Validate extension + * @param {array} extension + * @param {string} name + * @returns {{valid: boolean, error: string}} + */ +function validate(extension, name) { + 'use strict'; + + var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension', + ret = { + valid: true, + error: '' + }; + + if (!showdown.helper.isArray(extension)) { + extension = [extension]; + } + + for (var i = 0; i < extension.length; ++i) { + var baseMsg = errMsg + ' sub-extension ' + i + ': ', + ext = extension[i]; + if (typeof ext !== 'object') { + ret.valid = false; + ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given'; + return ret; + } + + if (!showdown.helper.isString(ext.type)) { + ret.valid = false; + ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given'; + return ret; + } + + var type = ext.type = ext.type.toLowerCase(); + + // normalize extension type + if (type === 'language') { + type = ext.type = 'lang'; + } + + if (type === 'html') { + type = ext.type = 'output'; + } + + if (type !== 'lang' && type !== 'output' && type !== 'listener') { + ret.valid = false; + ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang/language", "output/html" or "listener"'; + return ret; + } + + if (type === 'listener') { + if (showdown.helper.isUndefined(ext.listeners)) { + ret.valid = false; + ret.error = baseMsg + '. Extensions of type "listener" must have a property called "listeners"'; + return ret; + } + } else { + if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) { + ret.valid = false; + ret.error = baseMsg + type + ' extensions must define either a "regex" property or a "filter" method'; + return ret; + } + } + + if (ext.listeners) { + if (typeof ext.listeners !== 'object') { + ret.valid = false; + ret.error = baseMsg + '"listeners" property must be an object but ' + typeof ext.listeners + ' given'; + return ret; + } + for (var ln in ext.listeners) { + if (ext.listeners.hasOwnProperty(ln)) { + if (typeof ext.listeners[ln] !== 'function') { + ret.valid = false; + ret.error = baseMsg + '"listeners" property must be an hash of [event name]: [callback]. listeners.' + ln + + ' must be a function but ' + typeof ext.listeners[ln] + ' given'; + return ret; + } + } + } + } + + if (ext.filter) { + if (typeof ext.filter !== 'function') { + ret.valid = false; + ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given'; + return ret; + } + } else if (ext.regex) { + if (showdown.helper.isString(ext.regex)) { + ext.regex = new RegExp(ext.regex, 'g'); + } + if (!ext.regex instanceof RegExp) { + ret.valid = false; + ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' + typeof ext.regex + ' given'; + return ret; + } + if (showdown.helper.isUndefined(ext.replace)) { + ret.valid = false; + ret.error = baseMsg + '"regex" extensions must implement a replace string or function'; + return ret; + } + } + } + return ret; +} + +/** + * Validate extension + * @param {object} ext + * @returns {boolean} + */ +showdown.validateExtension = function (ext) { + 'use strict'; + + var validateExtension = validate(ext, null); + if (!validateExtension.valid) { + console.warn(validateExtension.error); + return false; + } + return true; +}; + +/** + * showdownjs helper functions + */ + +if (!showdown.hasOwnProperty('helper')) { + showdown.helper = {}; +} + +/** + * Check if var is string + * @static + * @param {string} a + * @returns {boolean} + */ +showdown.helper.isString = function isString(a) { + 'use strict'; + return (typeof a === 'string' || a instanceof String); +}; + +/** + * Check if var is a function + * @static + * @param {string} a + * @returns {boolean} + */ +showdown.helper.isFunction = function isFunction(a) { + 'use strict'; + var getType = {}; + return a && getType.toString.call(a) === '[object Function]'; +}; + +/** + * ForEach helper function + * @static + * @param {*} obj + * @param {function} callback + */ +showdown.helper.forEach = function forEach(obj, callback) { + 'use strict'; + if (typeof obj.forEach === 'function') { + obj.forEach(callback); + } else { + for (var i = 0; i < obj.length; i++) { + callback(obj[i], i, obj); + } + } +}; + +/** + * isArray helper function + * @static + * @param {*} a + * @returns {boolean} + */ +showdown.helper.isArray = function isArray(a) { + 'use strict'; + return a.constructor === Array; +}; + +/** + * Check if value is undefined + * @static + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + */ +showdown.helper.isUndefined = function isUndefined(value) { + 'use strict'; + return typeof value === 'undefined'; +}; + +/** + * Standardidize extension name + * @static + * @param {string} s extension name + * @returns {string} + */ +showdown.helper.stdExtName = function (s) { + 'use strict'; + return s.replace(/[_-]||\s/g, '').toLowerCase(); +}; + +function escapeCharactersCallback(wholeMatch, m1) { + 'use strict'; + var charCodeToEscape = m1.charCodeAt(0); + return '~E' + charCodeToEscape + 'E'; +} + +/** + * Callback used to escape characters when passing through String.replace + * @static + * @param {string} wholeMatch + * @param {string} m1 + * @returns {string} + */ +showdown.helper.escapeCharactersCallback = escapeCharactersCallback; + +/** + * Escape characters in a string + * @static + * @param {string} text + * @param {string} charsToEscape + * @param {boolean} afterBackslash + * @returns {XML|string|void|*} + */ +showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) { + 'use strict'; + // First we have to escape the escape characters so that + // we can build a character class out of them + var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])'; + + if (afterBackslash) { + regexString = '\\\\' + regexString; + } + + var regex = new RegExp(regexString, 'g'); + text = text.replace(regex, escapeCharactersCallback); + + return text; +}; + +var rgxFindMatchPos = function (str, left, right, flags) { + 'use strict'; + var f = flags || '', + g = f.indexOf('g') > -1, + x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, '')), + l = new RegExp(left, f.replace(/g/g, '')), + pos = [], + t, s, m, start, end; + + do { + t = 0; + while ((m = x.exec(str))) { + if (l.test(m[0])) { + if (!(t++)) { + s = x.lastIndex; + start = s - m[0].length; + } + } else if (t) { + if (!--t) { + end = m.index + m[0].length; + var obj = { + left: {start: start, end: s}, + match: {start: s, end: m.index}, + right: {start: m.index, end: end}, + wholeMatch: {start: start, end: end} + }; + pos.push(obj); + if (!g) { + return pos; + } + } + } + } + } while (t && (x.lastIndex = s)); + + return pos; +}; + +/** + * matchRecursiveRegExp + * + * (c) 2007 Steven Levithan + * MIT License + * + * Accepts a string to search, a left and right format delimiter + * as regex patterns, and optional regex flags. Returns an array + * of matches, allowing nested instances of left/right delimiters. + * Use the "g" flag to return all matches, otherwise only the + * first is returned. Be careful to ensure that the left and + * right format delimiters produce mutually exclusive matches. + * Backreferences are not supported within the right delimiter + * due to how it is internally combined with the left delimiter. + * When matching strings whose format delimiters are unbalanced + * to the left or right, the output is intentionally as a + * conventional regex library with recursion support would + * produce, e.g. "<" and ">" both produce ["x"] when using + * "<" and ">" as the delimiters (both strings contain a single, + * balanced instance of ""). + * + * examples: + * matchRecursiveRegExp("test", "\\(", "\\)") + * returns: [] + * matchRecursiveRegExp(">>t<>", "<", ">", "g") + * returns: ["t<>", ""] + * matchRecursiveRegExp("

test
", "]*>", "", "gi") + * returns: ["test"] + */ +showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) { + 'use strict'; + + var matchPos = rgxFindMatchPos (str, left, right, flags), + results = []; + + for (var i = 0; i < matchPos.length; ++i) { + results.push([ + str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end), + str.slice(matchPos[i].match.start, matchPos[i].match.end), + str.slice(matchPos[i].left.start, matchPos[i].left.end), + str.slice(matchPos[i].right.start, matchPos[i].right.end) + ]); + } + return results; +}; + +/** + * + * @param {string} str + * @param {string|function} replacement + * @param {string} left + * @param {string} right + * @param {string} flags + * @returns {string} + */ +showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) { + 'use strict'; + + if (!showdown.helper.isFunction(replacement)) { + var repStr = replacement; + replacement = function () { + return repStr; + }; + } + + var matchPos = rgxFindMatchPos(str, left, right, flags), + finalStr = str, + lng = matchPos.length; + + if (lng > 0) { + var bits = []; + if (matchPos[0].wholeMatch.start !== 0) { + bits.push(str.slice(0, matchPos[0].wholeMatch.start)); + } + for (var i = 0; i < lng; ++i) { + bits.push( + replacement( + str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end), + str.slice(matchPos[i].match.start, matchPos[i].match.end), + str.slice(matchPos[i].left.start, matchPos[i].left.end), + str.slice(matchPos[i].right.start, matchPos[i].right.end) + ) + ); + if (i < lng - 1) { + bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start)); + } + } + if (matchPos[lng - 1].wholeMatch.end < str.length) { + bits.push(str.slice(matchPos[lng - 1].wholeMatch.end)); + } + finalStr = bits.join(''); + } + return finalStr; +}; + +/** + * POLYFILLS + */ +if (showdown.helper.isUndefined(console)) { + console = { + warn: function (msg) { + 'use strict'; + alert(msg); + }, + log: function (msg) { + 'use strict'; + alert(msg); + }, + error: function (msg) { + 'use strict'; + throw msg; + } + }; +} + +/** + * Created by Estevao on 31-05-2015. + */ + +/** + * Showdown Converter class + * @class + * @param {object} [converterOptions] + * @returns {Converter} + */ +showdown.Converter = function (converterOptions) { + 'use strict'; + + var + /** + * Options used by this converter + * @private + * @type {{}} + */ + options = {}, + + /** + * Language extensions used by this converter + * @private + * @type {Array} + */ + langExtensions = [], + + /** + * Output modifiers extensions used by this converter + * @private + * @type {Array} + */ + outputModifiers = [], + + /** + * Event listeners + * @private + * @type {{}} + */ + listeners = {}; + + _constructor(); + + /** + * Converter constructor + * @private + */ + function _constructor() { + converterOptions = converterOptions || {}; + + for (var gOpt in globalOptions) { + if (globalOptions.hasOwnProperty(gOpt)) { + options[gOpt] = globalOptions[gOpt]; + } + } + + // Merge options + if (typeof converterOptions === 'object') { + for (var opt in converterOptions) { + if (converterOptions.hasOwnProperty(opt)) { + options[opt] = converterOptions[opt]; + } + } + } else { + throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions + + ' was passed instead.'); + } + + if (options.extensions) { + showdown.helper.forEach(options.extensions, _parseExtension); + } + } + + /** + * Parse extension + * @param {*} ext + * @param {string} [name=''] + * @private + */ + function _parseExtension(ext, name) { + + name = name || null; + // If it's a string, the extension was previously loaded + if (showdown.helper.isString(ext)) { + ext = showdown.helper.stdExtName(ext); + name = ext; + + // LEGACY_SUPPORT CODE + if (showdown.extensions[ext]) { + console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' + + 'Please inform the developer that the extension should be updated!'); + legacyExtensionLoading(showdown.extensions[ext], ext); + return; + // END LEGACY SUPPORT CODE + + } else if (!showdown.helper.isUndefined(extensions[ext])) { + ext = extensions[ext]; + + } else { + throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.'); + } + } + + if (typeof ext === 'function') { + ext = ext(); + } + + if (!showdown.helper.isArray(ext)) { + ext = [ext]; + } + + var validExt = validate(ext, name); + if (!validExt.valid) { + throw Error(validExt.error); + } + + for (var i = 0; i < ext.length; ++i) { + switch (ext[i].type) { + + case 'lang': + langExtensions.push(ext[i]); + break; + + case 'output': + outputModifiers.push(ext[i]); + break; + } + if (ext[i].hasOwnProperty(listeners)) { + for (var ln in ext[i].listeners) { + if (ext[i].listeners.hasOwnProperty(ln)) { + listen(ln, ext[i].listeners[ln]); + } + } + } + } + + } + + /** + * LEGACY_SUPPORT + * @param {*} ext + * @param {string} name + */ + function legacyExtensionLoading(ext, name) { + if (typeof ext === 'function') { + ext = ext(new showdown.Converter()); + } + if (!showdown.helper.isArray(ext)) { + ext = [ext]; + } + var valid = validate(ext, name); + + if (!valid.valid) { + throw Error(valid.error); + } + + for (var i = 0; i < ext.length; ++i) { + switch (ext[i].type) { + case 'lang': + langExtensions.push(ext[i]); + break; + case 'output': + outputModifiers.push(ext[i]); + break; + default:// should never reach here + throw Error('Extension loader error: Type unrecognized!!!'); + } + } + } + + /** + * Listen to an event + * @param {string} name + * @param {function} callback + */ + function listen(name, callback) { + if (!showdown.helper.isString(name)) { + throw Error('Invalid argument in converter.listen() method: name must be a string, but ' + typeof name + ' given'); + } + + if (typeof callback !== 'function') { + throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given'); + } + + if (!listeners.hasOwnProperty(name)) { + listeners[name] = []; + } + listeners[name].push(callback); + } + + function rTrimInputText(text) { + var rsp = text.match(/^\s*/)[0].length, + rgx = new RegExp('^\\s{0,' + rsp + '}', 'gm'); + return text.replace(rgx, ''); + } + + /** + * Dispatch an event + * @private + * @param {string} evtName Event name + * @param {string} text Text + * @param {{}} options Converter Options + * @param {{}} globals + * @returns {string} + */ + this._dispatch = function dispatch (evtName, text, options, globals) { + if (listeners.hasOwnProperty(evtName)) { + for (var ei = 0; ei < listeners[evtName].length; ++ei) { + var nText = listeners[evtName][ei](evtName, text, this, options, globals); + if (nText && typeof nText !== 'undefined') { + text = nText; + } + } + } + return text; + }; + + /** + * Listen to an event + * @param {string} name + * @param {function} callback + * @returns {showdown.Converter} + */ + this.listen = function (name, callback) { + listen(name, callback); + return this; + }; + + /** + * Converts a markdown string into HTML + * @param {string} text + * @returns {*} + */ + this.makeHtml = function (text) { + //check if text is not falsy + if (!text) { + return text; + } + + var globals = { + gHtmlBlocks: [], + gHtmlMdBlocks: [], + gHtmlSpans: [], + gUrls: {}, + gTitles: {}, + gDimensions: {}, + gListLevel: 0, + hashLinkCounts: {}, + langExtensions: langExtensions, + outputModifiers: outputModifiers, + converter: this, + ghCodeBlocks: [] + }; + + // attacklab: Replace ~ with ~T + // This lets us use tilde as an escape char to avoid md5 hashes + // The choice of character is arbitrary; anything that isn't + // magic in Markdown will work. + text = text.replace(/~/g, '~T'); + + // attacklab: Replace $ with ~D + // RegExp interprets $ as a special character + // when it's in a replacement string + text = text.replace(/\$/g, '~D'); + + // Standardize line endings + text = text.replace(/\r\n/g, '\n'); // DOS to Unix + text = text.replace(/\r/g, '\n'); // Mac to Unix + + if (options.smartIndentationFix) { + text = rTrimInputText(text); + } + + // Make sure text begins and ends with a couple of newlines: + //text = '\n\n' + text + '\n\n'; + text = text; + // detab + text = showdown.subParser('detab')(text, options, globals); + + // stripBlankLines + text = showdown.subParser('stripBlankLines')(text, options, globals); + + //run languageExtensions + showdown.helper.forEach(langExtensions, function (ext) { + text = showdown.subParser('runExtension')(ext, text, options, globals); + }); + + // run the sub parsers + text = showdown.subParser('hashPreCodeTags')(text, options, globals); + text = showdown.subParser('githubCodeBlocks')(text, options, globals); + text = showdown.subParser('hashHTMLBlocks')(text, options, globals); + text = showdown.subParser('hashHTMLSpans')(text, options, globals); + text = showdown.subParser('stripLinkDefinitions')(text, options, globals); + text = showdown.subParser('blockGamut')(text, options, globals); + text = showdown.subParser('unhashHTMLSpans')(text, options, globals); + text = showdown.subParser('unescapeSpecialChars')(text, options, globals); + + // attacklab: Restore dollar signs + text = text.replace(/~D/g, '$$'); + + // attacklab: Restore tildes + text = text.replace(/~T/g, '~'); + + // Run output modifiers + showdown.helper.forEach(outputModifiers, function (ext) { + text = showdown.subParser('runExtension')(ext, text, options, globals); + }); + return text; + }; + + /** + * Set an option of this Converter instance + * @param {string} key + * @param {*} value + */ + this.setOption = function (key, value) { + options[key] = value; + }; + + /** + * Get the option of this Converter instance + * @param {string} key + * @returns {*} + */ + this.getOption = function (key) { + return options[key]; + }; + + /** + * Get the options of this Converter instance + * @returns {{}} + */ + this.getOptions = function () { + return options; + }; + + /** + * Add extension to THIS converter + * @param {{}} extension + * @param {string} [name=null] + */ + this.addExtension = function (extension, name) { + name = name || null; + _parseExtension(extension, name); + }; + + /** + * Use a global registered extension with THIS converter + * @param {string} extensionName Name of the previously registered extension + */ + this.useExtension = function (extensionName) { + _parseExtension(extensionName); + }; + + /** + * Set the flavor THIS converter should use + * @param {string} name + */ + this.setFlavor = function (name) { + if (flavor.hasOwnProperty(name)) { + var preset = flavor[name]; + for (var option in preset) { + if (preset.hasOwnProperty(option)) { + options[option] = preset[option]; + } + } + } + }; + + /** + * Remove an extension from THIS converter. + * Note: This is a costly operation. It's better to initialize a new converter + * and specify the extensions you wish to use + * @param {Array} extension + */ + this.removeExtension = function (extension) { + if (!showdown.helper.isArray(extension)) { + extension = [extension]; + } + for (var a = 0; a < extension.length; ++a) { + var ext = extension[a]; + for (var i = 0; i < langExtensions.length; ++i) { + if (langExtensions[i] === ext) { + langExtensions[i].splice(i, 1); + } + } + for (var ii = 0; ii < outputModifiers.length; ++i) { + if (outputModifiers[ii] === ext) { + outputModifiers[ii].splice(i, 1); + } + } + } + }; + + /** + * Get all extension of THIS converter + * @returns {{language: Array, output: Array}} + */ + this.getAllExtensions = function () { + return { + language: langExtensions, + output: outputModifiers + }; + }; +}; + +/** + * Turn Markdown link shortcuts into XHTML tags. + */ +showdown.subParser('anchors', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('anchors.before', text, options, globals); + + var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) { + if (showdown.helper.isUndefined(m7)) { + m7 = ''; + } + wholeMatch = m1; + var linkText = m2, + linkId = m3.toLowerCase(), + url = m4, + title = m7; + + if (!url) { + if (!linkId) { + // lower-case and turn embedded newlines into spaces + linkId = linkText.toLowerCase().replace(/ ?\n/g, ' '); + } + url = '#' + linkId; + + if (!showdown.helper.isUndefined(globals.gUrls[linkId])) { + url = globals.gUrls[linkId]; + if (!showdown.helper.isUndefined(globals.gTitles[linkId])) { + title = globals.gTitles[linkId]; + } + } else { + if (wholeMatch.search(/\(\s*\)$/m) > -1) { + // Special case for explicit empty url + url = ''; + } else { + return wholeMatch; + } + } + } + + url = showdown.helper.escapeCharacters(url, '*_', false); + var result = ''; + + return result; + }; + + // First, handle reference-style links: [link text] [id] + /* + text = text.replace(/ + ( // wrap whole match in $1 + \[ + ( + (?: + \[[^\]]*\] // allow brackets nested one level + | + [^\[] // or anything else + )* + ) + \] + + [ ]? // one optional space + (?:\n[ ]*)? // one optional newline followed by spaces + + \[ + (.*?) // id = $3 + \] + )()()()() // pad remaining backreferences + /g,_DoAnchors_callback); + */ + text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)][ ]?(?:\n[ ]*)?\[(.*?)])()()()()/g, writeAnchorTag); + + // + // Next, inline-style links: [link text](url "optional title") + // + + /* + text = text.replace(/ + ( // wrap whole match in $1 + \[ + ( + (?: + \[[^\]]*\] // allow brackets nested one level + | + [^\[\]] // or anything else + ) + ) + \] + \( // literal paren + [ \t]* + () // no id, so leave $3 empty + ? // href = $4 + [ \t]* + ( // $5 + (['"]) // quote char = $6 + (.*?) // Title = $7 + \6 // matching quote + [ \t]* // ignore any spaces/tabs between closing quote and ) + )? // title is optional + \) + ) + /g,writeAnchorTag); + */ + text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, + writeAnchorTag); + + // + // Last, handle reference-style shortcuts: [link text] + // These must come last in case you've also got [link test][1] + // or [link test](/foo) + // + + /* + text = text.replace(/ + ( // wrap whole match in $1 + \[ + ([^\[\]]+) // link text = $2; can't contain '[' or ']' + \] + )()()()()() // pad rest of backreferences + /g, writeAnchorTag); + */ + text = text.replace(/(\[([^\[\]]+)])()()()()()/g, writeAnchorTag); + + text = globals.converter._dispatch('anchors.after', text, options, globals); + return text; +}); + +showdown.subParser('autoLinks', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('autoLinks.before', text, options, globals); + + var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi, + delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi, + simpleMailRegex = /(?:^|[ \n\t])([A-Za-z0-9!#$%&'*+-/=?^_`\{|}~\.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?:$|[ \n\t])/gi, + delimMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi; + + text = text.replace(delimUrlRegex, replaceLink); + text = text.replace(delimMailRegex, replaceMail); + // simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi, + // Email addresses: + + if (options.simplifiedAutoLink) { + text = text.replace(simpleURLRegex, replaceLink); + text = text.replace(simpleMailRegex, replaceMail); + } + + function replaceLink(wm, link) { + var lnkTxt = link; + if (/^www\./i.test(link)) { + link = link.replace(/^www\./i, 'http://www.'); + } + return '' + lnkTxt + ''; + } + + function replaceMail(wholeMatch, m1) { + var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1); + return showdown.subParser('encodeEmailAddress')(unescapedStr); + } + + text = globals.converter._dispatch('autoLinks.after', text, options, globals); + + return text; +}); + +/** + * These are all the transformations that form block-level + * tags like paragraphs, headers, and list items. + */ +showdown.subParser('blockGamut', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('blockGamut.before', text, options, globals); + + // we parse blockquotes first so that we can have headings and hrs + // inside blockquotes + text = showdown.subParser('blockQuotes')(text, options, globals); + text = showdown.subParser('headers')(text, options, globals); + + // Do Horizontal Rules: + var key = showdown.subParser('hashBlock')('
', options, globals); + text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key); + text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key); + text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key); + + text = showdown.subParser('lists')(text, options, globals); + text = showdown.subParser('codeBlocks')(text, options, globals); + text = showdown.subParser('tables')(text, options, globals); + + // We already ran _HashHTMLBlocks() before, in Markdown(), but that + // was to escape raw HTML in the original Markdown source. This time, + // we're escaping the markup we've just created, so that we don't wrap + //

tags around block-level tags. + text = showdown.subParser('hashHTMLBlocks')(text, options, globals); + text = showdown.subParser('paragraphs')(text, options, globals); + + text = globals.converter._dispatch('blockGamut.after', text, options, globals); + + return text; +}); + +showdown.subParser('blockQuotes', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('blockQuotes.before', text, options, globals); + /* + text = text.replace(/ + ( // Wrap whole match in $1 + ( + ^[ \t]*>[ \t]? // '>' at the start of a line + .+\n // rest of the first line + (.+\n)* // subsequent consecutive lines + \n* // blanks + )+ + ) + /gm, function(){...}); + */ + + text = text.replace(/((^[ \t]{0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) { + var bq = m1; + + // attacklab: hack around Konqueror 3.5.4 bug: + // "----------bug".replace(/^-/g,"") == "bug" + bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0'); // trim one level of quoting + + // attacklab: clean up hack + bq = bq.replace(/~0/g, ''); + + bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines + bq = showdown.subParser('githubCodeBlocks')(bq, options, globals); + bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse + + bq = bq.replace(/(^|\n)/g, '$1 '); + // These leading spaces screw with

 content, so we need to fix that:
+    bq = bq.replace(/(\s*
[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
+      var pre = m1;
+      // attacklab: hack around Konqueror 3.5.4 bug:
+      pre = pre.replace(/^  /mg, '~0');
+      pre = pre.replace(/~0/g, '');
+      return pre;
+    });
+
+    return showdown.subParser('hashBlock')('
\n' + bq + '\n
', options, globals); + }); + + text = globals.converter._dispatch('blockQuotes.after', text, options, globals); + return text; +}); + +/** + * Process Markdown `
` blocks.
+ */
+showdown.subParser('codeBlocks', function (text, options, globals) {
+  'use strict';
+
+  text = globals.converter._dispatch('codeBlocks.before', text, options, globals);
+  /*
+   text = text.replace(text,
+   /(?:\n\n|^)
+   (								// $1 = the code block -- one or more lines, starting with a space/tab
+   (?:
+   (?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
+   .*\n+
+   )+
+   )
+   (\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
+   /g,function(){...});
+   */
+
+  // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
+  text += '~0';
+
+  var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;
+  text = text.replace(pattern, function (wholeMatch, m1, m2) {
+    var codeblock = m1,
+        nextChar = m2,
+        end = '\n';
+
+    codeblock = showdown.subParser('outdent')(codeblock);
+    codeblock = showdown.subParser('encodeCode')(codeblock);
+    codeblock = showdown.subParser('detab')(codeblock);
+    codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
+    codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
+
+    if (options.omitExtraWLInCodeBlocks) {
+      end = '';
+    }
+
+    codeblock = '
' + codeblock + end + '
'; + + return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar; + }); + + // attacklab: strip sentinel + text = text.replace(/~0/, ''); + + text = globals.converter._dispatch('codeBlocks.after', text, options, globals); + return text; +}); + +/** + * + * * Backtick quotes are used for spans. + * + * * You can use multiple backticks as the delimiters if you want to + * include literal backticks in the code span. So, this input: + * + * Just type ``foo `bar` baz`` at the prompt. + * + * Will translate to: + * + *

Just type foo `bar` baz at the prompt.

+ * + * There's no arbitrary limit to the number of backticks you + * can use as delimters. If you need three consecutive backticks + * in your code, use four for delimiters, etc. + * + * * You can use spaces to get literal backticks at the edges: + * + * ... type `` `bar` `` ... + * + * Turns to: + * + * ... type `bar` ... + */ +showdown.subParser('codeSpans', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('codeSpans.before', text, options, globals); + + /* + text = text.replace(/ + (^|[^\\]) // Character before opening ` can't be a backslash + (`+) // $2 = Opening run of ` + ( // $3 = The code block + [^\r]*? + [^`] // attacklab: work around lack of lookbehind + ) + \2 // Matching closer + (?!`) + /gm, function(){...}); + */ + + if (typeof(text) === 'undefined') { + text = ''; + } + text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, + function (wholeMatch, m1, m2, m3) { + var c = m3; + c = c.replace(/^([ \t]*)/g, ''); // leading whitespace + c = c.replace(/[ \t]*$/g, ''); // trailing whitespace + c = showdown.subParser('encodeCode')(c); + return m1 + '' + c + ''; + } + ); + + text = globals.converter._dispatch('codeSpans.after', text, options, globals); + return text; +}); + +/** + * Convert all tabs to spaces + */ +showdown.subParser('detab', function (text) { + 'use strict'; + + // expand first n-1 tabs + text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width + + // replace the nth with two sentinels + text = text.replace(/\t/g, '~A~B'); + + // use the sentinel to anchor our regex so it doesn't explode + text = text.replace(/~B(.+?)~A/g, function (wholeMatch, m1) { + var leadingText = m1, + numSpaces = 4 - leadingText.length % 4; // g_tab_width + + // there *must* be a better way to do this: + for (var i = 0; i < numSpaces; i++) { + leadingText += ' '; + } + + return leadingText; + }); + + // clean up sentinels + text = text.replace(/~A/g, ' '); // g_tab_width + text = text.replace(/~B/g, ''); + + return text; + +}); + +/** + * Smart processing for ampersands and angle brackets that need to be encoded. + */ +showdown.subParser('encodeAmpsAndAngles', function (text) { + 'use strict'; + // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: + // http://bumppo.net/projects/amputator/ + text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&'); + + // Encode naked <'s + text = text.replace(/<(?![a-z\/?\$!])/gi, '<'); + + return text; +}); + +/** + * Returns the string, with after processing the following backslash escape sequences. + * + * attacklab: The polite way to do this is with the new escapeCharacters() function: + * + * text = escapeCharacters(text,"\\",true); + * text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); + * + * ...but we're sidestepping its use of the (slow) RegExp constructor + * as an optimization for Firefox. This function gets called a LOT. + */ +showdown.subParser('encodeBackslashEscapes', function (text) { + 'use strict'; + text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback); + text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, showdown.helper.escapeCharactersCallback); + return text; +}); + +/** + * Encode/escape certain characters inside Markdown code runs. + * The point is that in code, these characters are literals, + * and lose their special Markdown meanings. + */ +showdown.subParser('encodeCode', function (text) { + 'use strict'; + + // Encode all ampersands; HTML entities are not + // entities within a Markdown code span. + text = text.replace(/&/g, '&'); + + // Do the angle bracket song and dance: + text = text.replace(//g, '>'); + + // Now, escape characters that are magic in Markdown: + text = showdown.helper.escapeCharacters(text, '*_{}[]\\', false); + + // jj the line above breaks this: + //--- + //* Item + // 1. Subitem + // special char: * + // --- + + return text; +}); + +/** + * Input: an email address, e.g. "foo@example.com" + * + * Output: the email address as a mailto link, with each character + * of the address encoded as either a decimal or hex entity, in + * the hopes of foiling most address harvesting spam bots. E.g.: + * + * foo + * @example.com + * + * Based on a filter by Matthew Wickline, posted to the BBEdit-Talk + * mailing list: + * + */ +showdown.subParser('encodeEmailAddress', function (addr) { + 'use strict'; + + var encode = [ + function (ch) { + return '&#' + ch.charCodeAt(0) + ';'; + }, + function (ch) { + return '&#x' + ch.charCodeAt(0).toString(16) + ';'; + }, + function (ch) { + return ch; + } + ]; + + addr = 'mailto:' + addr; + + addr = addr.replace(/./g, function (ch) { + if (ch === '@') { + // this *must* be encoded. I insist. + ch = encode[Math.floor(Math.random() * 2)](ch); + } else if (ch !== ':') { + // leave ':' alone (to spot mailto: later) + var r = Math.random(); + // roughly 10% raw, 45% hex, 45% dec + ch = ( + r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch) + ); + } + return ch; + }); + + addr = '' + addr + ''; + addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part + + return addr; +}); + +/** + * Within tags -- meaning between < and > -- encode [\ ` * _] so they + * don't conflict with their use in Markdown for code, italics and strong. + */ +showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text) { + 'use strict'; + + // Build a regex to find HTML tags and comments. See Friedl's + // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. + var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; + + text = text.replace(regex, function (wholeMatch) { + var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`'); + tag = showdown.helper.escapeCharacters(tag, '\\`*_', false); + return tag; + }); + + return text; +}); + +/** + * Handle github codeblocks prior to running HashHTML so that + * HTML contained within the codeblock gets escaped properly + * Example: + * ```ruby + * def hello_world(x) + * puts "Hello, #{x}" + * end + * ``` + */ +showdown.subParser('githubCodeBlocks', function (text, options, globals) { + 'use strict'; + + // early exit if option is not enabled + if (!options.ghCodeBlocks) { + return text; + } + + text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals); + + text += '~0'; + + text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) { + var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n'; + + // First parse the github code block + codeblock = showdown.subParser('encodeCode')(codeblock); + codeblock = showdown.subParser('detab')(codeblock); + codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines + codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace + + codeblock = '
' + codeblock + end + '
'; + + codeblock = showdown.subParser('hashBlock')(codeblock, options, globals); + + // Since GHCodeblocks can be false positives, we need to + // store the primitive text and the parsed text in a global var, + // and then return a token + return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n'; + }); + + // attacklab: strip sentinel + text = text.replace(/~0/, ''); + + return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals); +}); + +showdown.subParser('hashBlock', function (text, options, globals) { + 'use strict'; + text = text.replace(/(^\n+|\n+$)/g, ''); + return '\n\n~K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n'; +}); + +showdown.subParser('hashElement', function (text, options, globals) { + 'use strict'; + + return function (wholeMatch, m1) { + var blockText = m1; + + // Undo double lines + blockText = blockText.replace(/\n\n/g, '\n'); + blockText = blockText.replace(/^\n/, ''); + + // strip trailing blank lines + blockText = blockText.replace(/\n+$/g, ''); + + // Replace the element text with a marker ("~KxK" where x is its key) + blockText = '\n\n~K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n'; + + return blockText; + }; +}); + +showdown.subParser('hashHTMLBlocks', function (text, options, globals) { + 'use strict'; + + var blockTags = [ + 'pre', + 'div', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'blockquote', + 'table', + 'dl', + 'ol', + 'ul', + 'script', + 'noscript', + 'form', + 'fieldset', + 'iframe', + 'math', + 'style', + 'section', + 'header', + 'footer', + 'nav', + 'article', + 'aside', + 'address', + 'audio', + 'canvas', + 'figure', + 'hgroup', + 'output', + 'video', + 'p' + ], + repFunc = function (wholeMatch, match, left, right) { + var txt = wholeMatch; + // check if this html element is marked as markdown + // if so, it's contents should be parsed as markdown + if (left.search(/\bmarkdown\b/) !== -1) { + txt = left + globals.converter.makeHtml(match) + right; + } + return '\n\n~K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n'; + }; + + for (var i = 0; i < blockTags.length; ++i) { + text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}<' + blockTags[i] + '\\b[^>]*>', '', 'gim'); + } + + // HR SPECIAL CASE + text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, + showdown.subParser('hashElement')(text, options, globals)); + + // Special case for standalone HTML comments: + text = text.replace(/()/g, + showdown.subParser('hashElement')(text, options, globals)); + + // PHP and ASP-style processor instructions ( and <%...%>) + text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, + showdown.subParser('hashElement')(text, options, globals)); + return text; +}); + +/** + * Hash span elements that should not be parsed as markdown + */ +showdown.subParser('hashHTMLSpans', function (text, config, globals) { + 'use strict'; + + var matches = showdown.helper.matchRecursiveRegExp(text, ']*>', '', 'gi'); + + for (var i = 0; i < matches.length; ++i) { + text = text.replace(matches[i][0], '~L' + (globals.gHtmlSpans.push(matches[i][0]) - 1) + 'L'); + } + return text; +}); + +/** + * Unhash HTML spans + */ +showdown.subParser('unhashHTMLSpans', function (text, config, globals) { + 'use strict'; + + for (var i = 0; i < globals.gHtmlSpans.length; ++i) { + text = text.replace('~L' + i + 'L', globals.gHtmlSpans[i]); + } + + return text; +}); + +/** + * Hash span elements that should not be parsed as markdown + */ +showdown.subParser('hashPreCodeTags', function (text, config, globals) { + 'use strict'; + + var repFunc = function (wholeMatch, match, left, right) { + // encode html entities + var codeblock = left + showdown.subParser('encodeCode')(match) + right; + return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n'; + }; + + text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}]*>\\s*]*>', '^(?: |\\t){0,3}\\s*
', 'gim'); + return text; +}); + +showdown.subParser('headers', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('headers.before', text, options, globals); + + var prefixHeader = options.prefixHeaderId, + headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart), + + // Set text-style headers: + // Header 1 + // ======== + // + // Header 2 + // -------- + // + setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm, + setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm; + + text = text.replace(setextRegexH1, function (wholeMatch, m1) { + + var spanGamut = showdown.subParser('spanGamut')(m1, options, globals), + hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"', + hLevel = headerLevelStart, + hashBlock = '' + spanGamut + ''; + return showdown.subParser('hashBlock')(hashBlock, options, globals); + }); + + text = text.replace(setextRegexH2, function (matchFound, m1) { + var spanGamut = showdown.subParser('spanGamut')(m1, options, globals), + hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"', + hLevel = headerLevelStart + 1, + hashBlock = '' + spanGamut + ''; + return showdown.subParser('hashBlock')(hashBlock, options, globals); + }); + + // atx-style headers: + // # Header 1 + // ## Header 2 + // ## Header 2 with closing hashes ## + // ... + // ###### Header 6 + // + text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, function (wholeMatch, m1, m2) { + var span = showdown.subParser('spanGamut')(m2, options, globals), + hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"', + hLevel = headerLevelStart - 1 + m1.length, + header = '' + span + ''; + + return showdown.subParser('hashBlock')(header, options, globals); + }); + + function headerId(m) { + var title, escapedId = m.replace(/[^\w]/g, '').toLowerCase(); + + if (globals.hashLinkCounts[escapedId]) { + title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++); + } else { + title = escapedId; + globals.hashLinkCounts[escapedId] = 1; + } + + // Prefix id to prevent causing inadvertent pre-existing style matches. + if (prefixHeader === true) { + prefixHeader = 'section'; + } + + if (showdown.helper.isString(prefixHeader)) { + return prefixHeader + title; + } + return title; + } + + text = globals.converter._dispatch('headers.after', text, options, globals); + return text; +}); + +/** + * Turn Markdown image shortcuts into tags. + */ +showdown.subParser('images', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('images.before', text, options, globals); + + var inlineRegExp = /!\[(.*?)]\s?\([ \t]*()?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g, + referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g; + + function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) { + + var gUrls = globals.gUrls, + gTitles = globals.gTitles, + gDims = globals.gDimensions; + + linkId = linkId.toLowerCase(); + + if (!title) { + title = ''; + } + + if (url === '' || url === null) { + if (linkId === '' || linkId === null) { + // lower-case and turn embedded newlines into spaces + linkId = altText.toLowerCase().replace(/ ?\n/g, ' '); + } + url = '#' + linkId; + + if (!showdown.helper.isUndefined(gUrls[linkId])) { + url = gUrls[linkId]; + if (!showdown.helper.isUndefined(gTitles[linkId])) { + title = gTitles[linkId]; + } + if (!showdown.helper.isUndefined(gDims[linkId])) { + width = gDims[linkId].width; + height = gDims[linkId].height; + } + } else { + return wholeMatch; + } + } + + altText = altText.replace(/"/g, '"'); + altText = showdown.helper.escapeCharacters(altText, '*_', false); + url = showdown.helper.escapeCharacters(url, '*_', false); + var result = '' + altText + 'x "optional title") + text = text.replace(inlineRegExp, writeImageTag); + + text = globals.converter._dispatch('images.after', text, options, globals); + return text; +}); + +showdown.subParser('italicsAndBold', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('italicsAndBold.before', text, options, globals); + + if (options.literalMidWordUnderscores) { + //underscores + // Since we are consuming a \s character, we need to add it + text = text.replace(/(^|\s|>|\b)__(?=\S)([\s\S]+?)__(?=\b|<|\s|$)/gm, '$1$2'); + text = text.replace(/(^|\s|>|\b)_(?=\S)([\s\S]+?)_(?=\b|<|\s|$)/gm, '$1$2'); + //asterisks + text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, '$2'); + text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, '$2'); + + } else { + // must go first: + text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '$2'); + text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '$2'); + } + + text = globals.converter._dispatch('italicsAndBold.after', text, options, globals); + return text; +}); + +/** + * Form HTML ordered (numbered) and unordered (bulleted) lists. + */ +showdown.subParser('lists', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('lists.before', text, options, globals); + /** + * Process the contents of a single ordered or unordered list, splitting it + * into individual list items. + * @param {string} listStr + * @param {boolean} trimTrailing + * @returns {string} + */ + function processListItems (listStr, trimTrailing) { + // The $g_list_level global keeps track of when we're inside a list. + // Each time we enter a list, we increment it; when we leave a list, + // we decrement. If it's zero, we're not in a list anymore. + // + // We do this because when we're not inside a list, we want to treat + // something like this: + // + // I recommend upgrading to version + // 8. Oops, now this line is treated + // as a sub-list. + // + // As a single paragraph, despite the fact that the second line starts + // with a digit-period-space sequence. + // + // Whereas when we're inside a list (or sub-list), that line will be + // treated as the start of a sub-list. What a kludge, huh? This is + // an aspect of Markdown's syntax that's hard to parse perfectly + // without resorting to mind-reading. Perhaps the solution is to + // change the syntax rules such that sub-lists must start with a + // starting cardinal number; e.g. "1." or "a.". + globals.gListLevel++; + + // trim trailing blank lines: + listStr = listStr.replace(/\n{2,}$/, '\n'); + + // attacklab: add sentinel to emulate \z + listStr += '~0'; + + var rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, + isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr)); + + listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) { + checked = (checked && checked.trim() !== ''); + var item = showdown.subParser('outdent')(m4, options, globals), + bulletStyle = ''; + + // Support for github tasklists + if (taskbtn && options.tasklists) { + bulletStyle = ' class="task-list-item" style="list-style-type: none;"'; + item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () { + var otp = ' -1)) { + item = showdown.subParser('githubCodeBlocks')(item, options, globals); + item = showdown.subParser('blockGamut')(item, options, globals); + } else { + // Recursion for sub-lists: + item = showdown.subParser('lists')(item, options, globals); + item = item.replace(/\n$/, ''); // chomp(item) + if (isParagraphed) { + item = showdown.subParser('paragraphs')(item, options, globals); + } else { + item = showdown.subParser('spanGamut')(item, options, globals); + } + } + item = '\n' + item + '\n'; + return item; + }); + + // attacklab: strip sentinel + listStr = listStr.replace(/~0/g, ''); + + globals.gListLevel--; + + if (trimTrailing) { + listStr = listStr.replace(/\s+$/, ''); + } + + return listStr; + } + + /** + * Check and parse consecutive lists (better fix for issue #142) + * @param {string} list + * @param {string} listType + * @param {boolean} trimTrailing + * @returns {string} + */ + function parseConsecutiveLists(list, listType, trimTrailing) { + // check if we caught 2 or more consecutive lists by mistake + // we use the counterRgx, meaning if listType is UL we look for UL and vice versa + var counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm, + subLists = [], + result = ''; + + if (list.search(counterRxg) !== -1) { + (function parseCL(txt) { + var pos = txt.search(counterRxg); + if (pos !== -1) { + // slice + result += '\n\n<' + listType + '>' + processListItems(txt.slice(0, pos), !!trimTrailing) + '\n\n'; + + // invert counterType and listType + listType = (listType === 'ul') ? 'ol' : 'ul'; + counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm; + + //recurse + parseCL(txt.slice(pos)); + } else { + result += '\n\n<' + listType + '>' + processListItems(txt, !!trimTrailing) + '\n\n'; + } + })(list); + for (var i = 0; i < subLists.length; ++i) { + + } + } else { + result = '\n\n<' + listType + '>' + processListItems(list, !!trimTrailing) + '\n\n'; + } + + return result; + } + + // attacklab: add sentinel to hack around khtml/safari bug: + // http://bugs.webkit.org/show_bug.cgi?id=11231 + text += '~0'; + + // Re-usable pattern to match any entire ul or ol list: + var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; + + if (globals.gListLevel) { + text = text.replace(wholeList, function (wholeMatch, list, m2) { + var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; + return parseConsecutiveLists(list, listType, true); + }); + } else { + wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; + //wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g; + text = text.replace(wholeList, function (wholeMatch, m1, list, m3) { + + var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; + return parseConsecutiveLists(list, listType); + }); + } + + // attacklab: strip sentinel + text = text.replace(/~0/, ''); + + text = globals.converter._dispatch('lists.after', text, options, globals); + return text; +}); + +/** + * Remove one level of line-leading tabs or spaces + */ +showdown.subParser('outdent', function (text) { + 'use strict'; + + // attacklab: hack around Konqueror 3.5.4 bug: + // "----------bug".replace(/^-/g,"") == "bug" + text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width + + // attacklab: clean up hack + text = text.replace(/~0/g, ''); + + return text; +}); + +/** + * + */ +showdown.subParser('paragraphs', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('paragraphs.before', text, options, globals); + // Strip leading and trailing lines: + text = text.replace(/^\n+/g, ''); + text = text.replace(/\n+$/g, ''); + + var grafs = text.split(/\n{2,}/g), + grafsOut = [], + end = grafs.length; // Wrap

tags + + for (var i = 0; i < end; i++) { + var str = grafs[i]; + // if this is an HTML marker, copy it + if (str.search(/~(K|G)(\d+)\1/g) >= 0) { + grafsOut.push(str); + } else { + str = showdown.subParser('spanGamut')(str, options, globals); + str = str.replace(/^([ \t]*)/g, '

'); + str += '

'; + grafsOut.push(str); + } + } + + /** Unhashify HTML blocks */ + end = grafsOut.length; + for (i = 0; i < end; i++) { + var blockText = '', + grafsOutIt = grafsOut[i], + codeFlag = false; + // if this is a marker for an html block... + while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) { + var delim = RegExp.$1, + num = RegExp.$2; + + if (delim === 'K') { + blockText = globals.gHtmlBlocks[num]; + } else { + // we need to check if ghBlock is a false positive + if (codeFlag) { + // use encoded version of all text + blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text); + } else { + blockText = globals.ghCodeBlocks[num].codeblock; + } + } + blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs + + grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText); + // Check if grafsOutIt is a pre->code + if (/^]*>\s*]*>/.test(grafsOutIt)) { + codeFlag = true; + } + } + grafsOut[i] = grafsOutIt; + } + text = grafsOut.join('\n\n'); + // Strip leading and trailing lines: + text = text.replace(/^\n+/g, ''); + text = text.replace(/\n+$/g, ''); + return globals.converter._dispatch('paragraphs.after', text, options, globals); +}); + +/** + * Run extension + */ +showdown.subParser('runExtension', function (ext, text, options, globals) { + 'use strict'; + + if (ext.filter) { + text = ext.filter(text, globals.converter, options); + + } else if (ext.regex) { + // TODO remove this when old extension loading mechanism is deprecated + var re = ext.regex; + if (!re instanceof RegExp) { + re = new RegExp(re, 'g'); + } + text = text.replace(re, ext.replace); + } + + return text; +}); + +/** + * These are all the transformations that occur *within* block-level + * tags like paragraphs, headers, and list items. + */ +showdown.subParser('spanGamut', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('spanGamut.before', text, options, globals); + text = showdown.subParser('codeSpans')(text, options, globals); + text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals); + text = showdown.subParser('encodeBackslashEscapes')(text, options, globals); + + // Process anchor and image tags. Images must come first, + // because ![foo][f] looks like an anchor. + text = showdown.subParser('images')(text, options, globals); + text = showdown.subParser('anchors')(text, options, globals); + + // Make links out of things like `` + // Must come after _DoAnchors(), because you can use < and > + // delimiters in inline links like [this](). + text = showdown.subParser('autoLinks')(text, options, globals); + text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals); + text = showdown.subParser('italicsAndBold')(text, options, globals); + text = showdown.subParser('strikethrough')(text, options, globals); + + // Do hard breaks: + text = text.replace(/ +\n/g, '
\n'); + + text = globals.converter._dispatch('spanGamut.after', text, options, globals); + return text; +}); + +showdown.subParser('strikethrough', function (text, options, globals) { + 'use strict'; + + if (options.strikethrough) { + text = globals.converter._dispatch('strikethrough.before', text, options, globals); + text = text.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g, '$1'); + text = globals.converter._dispatch('strikethrough.after', text, options, globals); + } + + return text; +}); + +/** + * Strip any lines consisting only of spaces and tabs. + * This makes subsequent regexs easier to write, because we can + * match consecutive blank lines with /\n+/ instead of something + * contorted like /[ \t]*\n+/ + */ +showdown.subParser('stripBlankLines', function (text) { + 'use strict'; + return text.replace(/^[ \t]+$/mg, ''); +}); + +/** + * Strips link definitions from text, stores the URLs and titles in + * hash references. + * Link defs are in the form: ^[id]: url "optional title" + * + * ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 + * [ \t]* + * \n? // maybe *one* newline + * [ \t]* + * ? // url = $2 + * [ \t]* + * \n? // maybe one newline + * [ \t]* + * (?: + * (\n*) // any lines skipped = $3 attacklab: lookbehind removed + * ["(] + * (.+?) // title = $4 + * [")] + * [ \t]* + * )? // title is optional + * (?:\n+|$) + * /gm, + * function(){...}); + * + */ +showdown.subParser('stripLinkDefinitions', function (text, options, globals) { + 'use strict'; + + var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm; + + // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug + text += '~0'; + + text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) { + linkId = linkId.toLowerCase(); + globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url); // Link IDs are case-insensitive + + if (blankLines) { + // Oops, found blank lines, so it's not a title. + // Put back the parenthetical statement we stole. + return blankLines + title; + + } else { + if (title) { + globals.gTitles[linkId] = title.replace(/"|'/g, '"'); + } + if (options.parseImgDimensions && width && height) { + globals.gDimensions[linkId] = { + width: width, + height: height + }; + } + } + // Completely remove the definition from the text + return ''; + }); + + // attacklab: strip sentinel + text = text.replace(/~0/, ''); + + return text; +}); + +showdown.subParser('tables', function (text, options, globals) { + 'use strict'; + + if (!options.tables) { + return text; + } + + var tableRgx = /^[ \t]{0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|~0)/gm; + + function parseStyles(sLine) { + if (/^:[ \t]*--*$/.test(sLine)) { + return ' style="text-align:left;"'; + } else if (/^--*[ \t]*:[ \t]*$/.test(sLine)) { + return ' style="text-align:right;"'; + } else if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) { + return ' style="text-align:center;"'; + } else { + return ''; + } + } + + function parseHeaders(header, style) { + var id = ''; + header = header.trim(); + if (options.tableHeaderId) { + id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"'; + } + header = showdown.subParser('spanGamut')(header, options, globals); + + return '' + header + '\n'; + } + + function parseCells(cell, style) { + var subText = showdown.subParser('spanGamut')(cell, options, globals); + return '' + subText + '\n'; + } + + function buildTable(headers, cells) { + var tb = '\n\n\n', + tblLgn = headers.length; + + for (var i = 0; i < tblLgn; ++i) { + tb += headers[i]; + } + tb += '\n\n\n'; + + for (i = 0; i < cells.length; ++i) { + tb += '\n'; + for (var ii = 0; ii < tblLgn; ++ii) { + tb += cells[i][ii]; + } + tb += '\n'; + } + tb += '\n
\n'; + return tb; + } + + text = globals.converter._dispatch('tables.before', text, options, globals); + + text = text.replace(tableRgx, function (rawTable) { + + var i, tableLines = rawTable.split('\n'); + + // strip wrong first and last column if wrapped tables are used + for (i = 0; i < tableLines.length; ++i) { + if (/^[ \t]{0,3}\|/.test(tableLines[i])) { + tableLines[i] = tableLines[i].replace(/^[ \t]{0,3}\|/, ''); + } + if (/\|[ \t]*$/.test(tableLines[i])) { + tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, ''); + } + } + + var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}), + rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}), + rawCells = [], + headers = [], + styles = [], + cells = []; + + tableLines.shift(); + tableLines.shift(); + + for (i = 0; i < tableLines.length; ++i) { + if (tableLines[i].trim() === '') { + continue; + } + rawCells.push( + tableLines[i] + .split('|') + .map(function (s) { + return s.trim(); + }) + ); + } + + if (rawHeaders.length < rawStyles.length) { + return rawTable; + } + + for (i = 0; i < rawStyles.length; ++i) { + styles.push(parseStyles(rawStyles[i])); + } + + for (i = 0; i < rawHeaders.length; ++i) { + if (showdown.helper.isUndefined(styles[i])) { + styles[i] = ''; + } + headers.push(parseHeaders(rawHeaders[i], styles[i])); + } + + for (i = 0; i < rawCells.length; ++i) { + var row = []; + for (var ii = 0; ii < headers.length; ++ii) { + if (showdown.helper.isUndefined(rawCells[i][ii])) { + + } + row.push(parseCells(rawCells[i][ii], styles[ii])); + } + cells.push(row); + } + + return buildTable(headers, cells); + }); + + text = globals.converter._dispatch('tables.after', text, options, globals); + + return text; +}); + +/** + * Swap back in all the special characters we've hidden. + */ +showdown.subParser('unescapeSpecialChars', function (text) { + 'use strict'; + + text = text.replace(/~E(\d+)E/g, function (wholeMatch, m1) { + var charCodeToReplace = parseInt(m1); + return String.fromCharCode(charCodeToReplace); + }); + return text; +}); +module.exports = showdown; diff --git a/renard-wx/lib/wxParse/wxDiscode.js b/renard-wx/lib/wxParse/wxDiscode.js new file mode 100644 index 00000000..e339f7a2 --- /dev/null +++ b/renard-wx/lib/wxParse/wxDiscode.js @@ -0,0 +1,206 @@ +// HTML 支持的数学符号 +function strNumDiscode(str){ + str = str.replace(/∀/g, '∀'); + str = str.replace(/∂/g, '∂'); + str = str.replace(/&exists;/g, '∃'); + str = str.replace(/∅/g, '∅'); + str = str.replace(/∇/g, '∇'); + str = str.replace(/∈/g, '∈'); + str = str.replace(/∉/g, '∉'); + str = str.replace(/∋/g, '∋'); + str = str.replace(/∏/g, '∏'); + str = str.replace(/∑/g, '∑'); + str = str.replace(/−/g, '−'); + str = str.replace(/∗/g, '∗'); + str = str.replace(/√/g, '√'); + str = str.replace(/∝/g, '∝'); + str = str.replace(/∞/g, '∞'); + str = str.replace(/∠/g, '∠'); + str = str.replace(/∧/g, '∧'); + str = str.replace(/∨/g, '∨'); + str = str.replace(/∩/g, '∩'); + str = str.replace(/∩/g, '∪'); + str = str.replace(/∫/g, '∫'); + str = str.replace(/∴/g, '∴'); + str = str.replace(/∼/g, '∼'); + str = str.replace(/≅/g, '≅'); + str = str.replace(/≈/g, '≈'); + str = str.replace(/≠/g, '≠'); + str = str.replace(/≤/g, '≤'); + str = str.replace(/≥/g, '≥'); + str = str.replace(/⊂/g, '⊂'); + str = str.replace(/⊃/g, '⊃'); + str = str.replace(/⊄/g, '⊄'); + str = str.replace(/⊆/g, '⊆'); + str = str.replace(/⊇/g, '⊇'); + str = str.replace(/⊕/g, '⊕'); + str = str.replace(/⊗/g, '⊗'); + str = str.replace(/⊥/g, '⊥'); + str = str.replace(/⋅/g, '⋅'); + return str; +} + +//HTML 支持的希腊字母 +function strGreeceDiscode(str){ + str = str.replace(/Α/g, 'Α'); + str = str.replace(/Β/g, 'Β'); + str = str.replace(/Γ/g, 'Γ'); + str = str.replace(/Δ/g, 'Δ'); + str = str.replace(/Ε/g, 'Ε'); + str = str.replace(/Ζ/g, 'Ζ'); + str = str.replace(/Η/g, 'Η'); + str = str.replace(/Θ/g, 'Θ'); + str = str.replace(/Ι/g, 'Ι'); + str = str.replace(/Κ/g, 'Κ'); + str = str.replace(/Λ/g, 'Λ'); + str = str.replace(/Μ/g, 'Μ'); + str = str.replace(/Ν/g, 'Ν'); + str = str.replace(/Ξ/g, 'Ν'); + str = str.replace(/Ο/g, 'Ο'); + str = str.replace(/Π/g, 'Π'); + str = str.replace(/Ρ/g, 'Ρ'); + str = str.replace(/Σ/g, 'Σ'); + str = str.replace(/Τ/g, 'Τ'); + str = str.replace(/Υ/g, 'Υ'); + str = str.replace(/Φ/g, 'Φ'); + str = str.replace(/Χ/g, 'Χ'); + str = str.replace(/Ψ/g, 'Ψ'); + str = str.replace(/Ω/g, 'Ω'); + + str = str.replace(/α/g, 'α'); + str = str.replace(/β/g, 'β'); + str = str.replace(/γ/g, 'γ'); + str = str.replace(/δ/g, 'δ'); + str = str.replace(/ε/g, 'ε'); + str = str.replace(/ζ/g, 'ζ'); + str = str.replace(/η/g, 'η'); + str = str.replace(/θ/g, 'θ'); + str = str.replace(/ι/g, 'ι'); + str = str.replace(/κ/g, 'κ'); + str = str.replace(/λ/g, 'λ'); + str = str.replace(/μ/g, 'μ'); + str = str.replace(/ν/g, 'ν'); + str = str.replace(/ξ/g, 'ξ'); + str = str.replace(/ο/g, 'ο'); + str = str.replace(/π/g, 'π'); + str = str.replace(/ρ/g, 'ρ'); + str = str.replace(/ς/g, 'ς'); + str = str.replace(/σ/g, 'σ'); + str = str.replace(/τ/g, 'τ'); + str = str.replace(/υ/g, 'υ'); + str = str.replace(/φ/g, 'φ'); + str = str.replace(/χ/g, 'χ'); + str = str.replace(/ψ/g, 'ψ'); + str = str.replace(/ω/g, 'ω'); + str = str.replace(/ϑ/g, 'ϑ'); + str = str.replace(/ϒ/g, 'ϒ'); + str = str.replace(/ϖ/g, 'ϖ'); + str = str.replace(/·/g, '·'); + return str; +} + +// + +function strcharacterDiscode(str){ + // 加入常用解析 + str = str.replace(/ /g, ' '); + str = str.replace(/"/g, '"'); + str = str.replace(/&/g, '&'); + // str = str.replace(/</g, '‹'); + // str = str.replace(/>/g, '›'); + + str = str.replace(/</g, '<'); + str = str.replace(/>/g, '>'); + + return str; +} + +// HTML 支持的其他实体 +function strOtherDiscode(str){ + str = str.replace(/Œ/g, 'Œ'); + str = str.replace(/œ/g, 'œ'); + str = str.replace(/Š/g, 'Š'); + str = str.replace(/š/g, 'š'); + str = str.replace(/Ÿ/g, 'Ÿ'); + str = str.replace(/ƒ/g, 'ƒ'); + str = str.replace(/ˆ/g, 'ˆ'); + str = str.replace(/˜/g, '˜'); + str = str.replace(/ /g, ''); + str = str.replace(/ /g, ''); + str = str.replace(/ /g, ''); + str = str.replace(/‌/g, ''); + str = str.replace(/‍/g, ''); + str = str.replace(/‎/g, ''); + str = str.replace(/‏/g, ''); + str = str.replace(/–/g, '–'); + str = str.replace(/—/g, '—'); + str = str.replace(/‘/g, '‘'); + str = str.replace(/’/g, '’'); + str = str.replace(/‚/g, '‚'); + str = str.replace(/“/g, '“'); + str = str.replace(/”/g, '”'); + str = str.replace(/„/g, '„'); + str = str.replace(/†/g, '†'); + str = str.replace(/‡/g, '‡'); + str = str.replace(/•/g, '•'); + str = str.replace(/…/g, '…'); + str = str.replace(/‰/g, '‰'); + str = str.replace(/′/g, '′'); + str = str.replace(/″/g, '″'); + str = str.replace(/‹/g, '‹'); + str = str.replace(/›/g, '›'); + str = str.replace(/‾/g, '‾'); + str = str.replace(/€/g, '€'); + str = str.replace(/™/g, '™'); + + str = str.replace(/←/g, '←'); + str = str.replace(/↑/g, '↑'); + str = str.replace(/→/g, '→'); + str = str.replace(/↓/g, '↓'); + str = str.replace(/↔/g, '↔'); + str = str.replace(/↵/g, '↵'); + str = str.replace(/⌈/g, '⌈'); + str = str.replace(/⌉/g, '⌉'); + + str = str.replace(/⌊/g, '⌊'); + str = str.replace(/⌋/g, '⌋'); + str = str.replace(/◊/g, '◊'); + str = str.replace(/♠/g, '♠'); + str = str.replace(/♣/g, '♣'); + str = str.replace(/♥/g, '♥'); + + str = str.replace(/♦/g, '♦'); + + return str; +} + +function strMoreDiscode(str){ + str = str.replace(/\r\n/g,""); + str = str.replace(/\n/g,""); + + str = str.replace(/code/g,"wxxxcode-style"); + return str; +} + +function strDiscode(str){ + str = strNumDiscode(str); + str = strGreeceDiscode(str); + str = strcharacterDiscode(str); + str = strOtherDiscode(str); + str = strMoreDiscode(str); + return str; +} +function urlToHttpUrl(url,rep){ + + var patt1 = new RegExp("^//"); + var result = patt1.test(url); + if(result){ + url = rep+":"+url; + } + return url; +} + +module.exports = { + strDiscode:strDiscode, + urlToHttpUrl:urlToHttpUrl +} \ No newline at end of file diff --git a/renard-wx/lib/wxParse/wxParse.js b/renard-wx/lib/wxParse/wxParse.js new file mode 100644 index 00000000..8adab502 --- /dev/null +++ b/renard-wx/lib/wxParse/wxParse.js @@ -0,0 +1,146 @@ +/** + * author: Di (微信小程序开发工程师) + * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) + * 垂直微信小程序开发交流社区 + * + * github地址: https://github.com/icindy/wxParse + * + * for: 微信小程序富文本解析 + * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 + */ + +/** + * utils函数引入 + **/ +import showdown from 'showdown.js'; +import HtmlToJson from 'html2json.js'; +/** + * 配置及公有属性 + **/ +/** + * 主函数入口区 + **/ +function wxParse(bindName = 'wxParseData', type='html', data='
数据不能为空
', target,imagePadding) { + var that = target; + var transData = {};//存放转化后的数据 + if (type == 'html') { + transData = HtmlToJson.html2json(data, bindName); + // console.log(JSON.stringify(transData, ' ', ' ')); + } else if (type == 'md' || type == 'markdown') { + var converter = new showdown.Converter(); + var html = converter.makeHtml(data); + transData = HtmlToJson.html2json(html, bindName); + // console.log(JSON.stringify(transData, ' ', ' ')); + } + transData.view = {}; + transData.view.imagePadding = 0; + if(typeof(imagePadding) != 'undefined'){ + transData.view.imagePadding = imagePadding + } + var bindData = {}; + bindData[bindName] = transData; + that.setData(bindData) + that.wxParseImgLoad = wxParseImgLoad; + that.wxParseImgTap = wxParseImgTap; +} +// 图片点击事件 +function wxParseImgTap(e) { + var that = this; + var nowImgUrl = e.target.dataset.src; + var tagFrom = e.target.dataset.from; + if (typeof (tagFrom) != 'undefined' && tagFrom.length > 0) { + wx.previewImage({ + current: nowImgUrl, // 当前显示图片的http链接 + urls: that.data[tagFrom].imageUrls // 需要预览的图片http链接列表 + }) + } +} + +/** + * 图片视觉宽高计算函数区 + **/ +function wxParseImgLoad(e) { + var that = this; + var tagFrom = e.target.dataset.from; + var idx = e.target.dataset.idx; + if (typeof (tagFrom) != 'undefined' && tagFrom.length > 0) { + calMoreImageInfo(e, idx, that, tagFrom) + } +} +// 假循环获取计算图片视觉最佳宽高 +function calMoreImageInfo(e, idx, that, bindName) { + var temData = that.data[bindName]; + if (temData.images.length == 0) { + return; + } + var temImages = temData.images; + //因为无法获取view宽度 需要自定义padding进行计算,稍后处理 + var recal = wxAutoImageCal(e.detail.width, e.detail.height,that,bindName); + temImages[idx].width = recal.imageWidth; + temImages[idx].height = recal.imageheight; + temData.images = temImages; + var bindData = {}; + bindData[bindName] = temData; + that.setData(bindData); +} + +// 计算视觉优先的图片宽高 +function wxAutoImageCal(originalWidth, originalHeight,that,bindName) { + //获取图片的原始长宽 + var windowWidth = 0, windowHeight = 0; + var autoWidth = 0, autoHeight = 0; + var results = {}; + wx.getSystemInfo({ + success: function (res) { + var padding = that.data[bindName].view.imagePadding; + windowWidth = res.windowWidth-2*padding; + windowHeight = res.windowHeight; + //判断按照那种方式进行缩放 + // console.log("windowWidth" + windowWidth); + if (originalWidth > windowWidth) {//在图片width大于手机屏幕width时候 + autoWidth = windowWidth; + // console.log("autoWidth" + autoWidth); + autoHeight = (autoWidth * originalHeight) / originalWidth; + // console.log("autoHeight" + autoHeight); + results.imageWidth = autoWidth; + results.imageheight = autoHeight; + } else {//否则展示原来的数据 + results.imageWidth = originalWidth; + results.imageheight = originalHeight; + } + } + }) + return results; +} + +function wxParseTemArray(temArrayName,bindNameReg,total,that){ + var array = []; + var temData = that.data; + var obj = null; + for(var i = 0; i < total; i++){ + var simArr = temData[bindNameReg+i].nodes; + array.push(simArr); + } + + temArrayName = temArrayName || 'wxParseTemArray'; + obj = JSON.parse('{"'+ temArrayName +'":""}'); + obj[temArrayName] = array; + that.setData(obj); +} + +/** + * 配置emojis + * + */ + +function emojisInit(reg='',baseSrc="/wxParse/emojis/",emojis){ + HtmlToJson.emojisInit(reg,baseSrc,emojis); +} + +module.exports = { + wxParse: wxParse, + wxParseTemArray:wxParseTemArray, + emojisInit:emojisInit +} + + diff --git a/renard-wx/lib/wxParse/wxParse.wxml b/renard-wx/lib/wxParse/wxParse.wxml new file mode 100644 index 00000000..963a1b19 --- /dev/null +++ b/renard-wx/lib/wxParse/wxParse.wxml @@ -0,0 +1,928 @@ + + + + + + + + + + + + +