├── README.md ├── app.js ├── app.json ├── app.wxss ├── content.js ├── content ├── 1content.js ├── 2content.js ├── 3content.js ├── 4content.js └── 5content.js ├── image ├── location.png ├── page1_bind.png ├── page1_checkin.png ├── page1_interactive.png └── page1_me.png ├── libs ├── qqmap-wx-jssdk.js └── qqmap-wx-jssdk.min.js ├── pages ├── checkin │ ├── checkin.js │ ├── checkin.json │ ├── checkin.wxml │ └── checkin.wxss ├── detail │ ├── detail.js │ ├── detail.json │ ├── detail.wxml │ └── detail.wxss ├── home │ ├── home.js │ ├── home.json │ ├── home.wxml │ └── home.wxss ├── index │ ├── index.js │ ├── index.wxml │ └── index.wxss ├── interactive │ ├── interactive.js │ ├── interactive.json │ ├── interactive.wxml │ └── interactive.wxss ├── logs │ ├── logs.js │ ├── logs.json │ ├── logs.wxml │ └── logs.wxss ├── postRequest │ ├── postRequest.js │ ├── postRequest.json │ ├── postRequest.wxml │ └── postRequest.wxss └── remarkRequest │ ├── remarkRequest.js │ ├── remarkRequest.json │ ├── remarkRequest.wxml │ └── remarkRequest.wxss ├── project.config.json └── utils ├── util.js └── wxParse ├── html2json.js ├── htmlparser.js ├── showdown.js ├── wxDiscode.js ├── wxParse.js ├── wxParse.wxml └── wxParse.wxss /README.md: -------------------------------------------------------------------------------- 1 | # WeChatProject 2 | 一个微信小程序:基于地理位置的签到打卡;可以发帖互动 3 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | //app.js 2 | App({ 3 | onLaunch: function () { 4 | // 展示本地存储能力 5 | var logs = wx.getStorageSync('logs') || [] 6 | logs.unshift(Date.now()) 7 | wx.setStorageSync('logs', logs) 8 | 9 | // 登录 10 | wx.login({ 11 | success: res => { 12 | // 发送 res.code 到后台换取 openId, sessionKey, unionId 13 | } 14 | }) 15 | // 获取用户信息 16 | wx.getSetting({ 17 | success: res => { 18 | if (res.authSetting['scope.userInfo']) { 19 | // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框 20 | wx.getUserInfo({ 21 | success: res => { 22 | // 可以将 res 发送给后台解码出 unionId 23 | this.globalData.userInfo = res.userInfo 24 | 25 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 26 | // 所以此处加入 callback 以防止这种情况 27 | if (this.userInfoReadyCallback) { 28 | this.userInfoReadyCallback(res) 29 | } 30 | } 31 | }) 32 | } 33 | } 34 | }) 35 | }, 36 | globalData: { 37 | userInfo: null, 38 | userBindInfo:null 39 | } 40 | }) -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages":[ 3 | "pages/index/index", 4 | "pages/logs/logs", 5 | "pages/checkin/checkin", 6 | "pages/home/home", 7 | "pages/interactive/interactive", 8 | "pages/postRequest/postRequest", 9 | "pages/remarkRequest/remarkRequest", 10 | "pages/detail/detail" 11 | ], 12 | "window":{ 13 | "backgroundTextStyle":"light", 14 | "navigationBarBackgroundColor": "#fff", 15 | "navigationBarTitleText": "WeChat", 16 | "navigationBarTextStyle":"black" 17 | }, 18 | "tabBar": { 19 | "color": "black", 20 | "list": [ 21 | { 22 | "pagePath": "pages/index/index", 23 | "text": "绑定信息", 24 | "iconPath": "image/page1_bind.png", 25 | "selectedIconPath": "image/page1_bind.png" 26 | }, 27 | { 28 | "pagePath": "pages/interactive/interactive", 29 | "text": "互动论坛", 30 | "iconPath": "image/page1_interactive.png", 31 | "selectedIconPath": "image/page1_interactive.png" 32 | }, 33 | { 34 | "pagePath": "pages/checkin/checkin", 35 | "text": "在线签到", 36 | "iconPath": "image/page1_checkin.png", 37 | "selectedIconPath": "image/page1_checkin.png" 38 | }, 39 | { 40 | "pagePath": "pages/home/home", 41 | "text": "与我相关", 42 | "iconPath": "image/page1_me.png", 43 | "selectedIconPath": "image/page1_me.png" 44 | } 45 | ] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | @import "./utils/wxParse/wxParse.wxss"; 3 | .container { 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | justify-content: space-between; 9 | padding: 50rpx 0; 10 | box-sizing: border-box; 11 | } 12 | 13 | 14 | @font-face { 15 | font-family: 'iconfont'; /* project id 518032 */ 16 | src: url('//at.alicdn.com/t/font_518032_t2q88z3jok8iwwmi.eot'); 17 | src: url('//at.alicdn.com/t/font_518032_t2q88z3jok8iwwmi.eot?#iefix') format('embedded-opentype'), 18 | url('//at.alicdn.com/t/font_518032_t2q88z3jok8iwwmi.woff') format('woff'), 19 | url('//at.alicdn.com/t/font_518032_t2q88z3jok8iwwmi.ttf') format('truetype'), 20 | url('//at.alicdn.com/t/font_518032_t2q88z3jok8iwwmi.svg#iconfont') format('svg'); 21 | } 22 | .iconfont { 23 | font-family:"iconfont" !important; 24 | font-size:30rpx; 25 | font-style:normal; 26 | -webkit-font-smoothing: antialiased; 27 | -moz-osx-font-smoothing: grayscale; 28 | } 29 | 30 | .icon-dibiao:before{content: '\e64d';} -------------------------------------------------------------------------------- /content.js: -------------------------------------------------------------------------------- 1 | var content = 'hellohello'; 2 | //var url ="https://www.iqidi.com"; 3 | 4 | module.exports={content} -------------------------------------------------------------------------------- /content/1content.js: -------------------------------------------------------------------------------- 1 | var content = '

我的第一个helloword程序

'; 2 | module.exports = { content } -------------------------------------------------------------------------------- /content/2content.js: -------------------------------------------------------------------------------- 1 | var content = '

解决了scanf函数疑惑

'; 2 | module.exports = { content } -------------------------------------------------------------------------------- /content/3content.js: -------------------------------------------------------------------------------- 1 | var content = '

精度问题总结

'; 2 | module.exports = { content } -------------------------------------------------------------------------------- /content/4content.js: -------------------------------------------------------------------------------- 1 | var content = '

结构体的使用

'; 2 | module.exports = { content } -------------------------------------------------------------------------------- /content/5content.js: -------------------------------------------------------------------------------- 1 | var content = '

指针学习

'; 2 | module.exports = { content } -------------------------------------------------------------------------------- /image/location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeeHaming/WeChatProject/a6c720ad6a66510f83623580c11e20418ceab2cd/image/location.png -------------------------------------------------------------------------------- /image/page1_bind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeeHaming/WeChatProject/a6c720ad6a66510f83623580c11e20418ceab2cd/image/page1_bind.png -------------------------------------------------------------------------------- /image/page1_checkin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeeHaming/WeChatProject/a6c720ad6a66510f83623580c11e20418ceab2cd/image/page1_checkin.png -------------------------------------------------------------------------------- /image/page1_interactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeeHaming/WeChatProject/a6c720ad6a66510f83623580c11e20418ceab2cd/image/page1_interactive.png -------------------------------------------------------------------------------- /image/page1_me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeeHaming/WeChatProject/a6c720ad6a66510f83623580c11e20418ceab2cd/image/page1_me.png -------------------------------------------------------------------------------- /libs/qqmap-wx-jssdk.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 微信小程序JavaScriptSDK 3 | * 4 | * @version 1.0 5 | * @date 2017-01-10 6 | * @author jaysonzhou@tencent.com 7 | */ 8 | 9 | var ERROR_CONF = { 10 | KEY_ERR: 311, 11 | KEY_ERR_MSG: 'key格式错误', 12 | PARAM_ERR: 310, 13 | PARAM_ERR_MSG: '请求参数信息有误', 14 | SYSTEM_ERR: 600, 15 | SYSTEM_ERR_MSG: '系统错误', 16 | WX_ERR_CODE: 1000, 17 | WX_OK_CODE: 200 18 | }; 19 | var BASE_URL = 'https://apis.map.qq.com/ws/'; 20 | var URL_SEARCH = BASE_URL + 'place/v1/search'; 21 | var URL_SUGGESTION = BASE_URL + 'place/v1/suggestion'; 22 | var URL_GET_GEOCODER = BASE_URL + 'geocoder/v1/'; 23 | var URL_CITY_LIST = BASE_URL + 'district/v1/list'; 24 | var URL_AREA_LIST = BASE_URL + 'district/v1/getchildren'; 25 | var URL_DISTANCE = BASE_URL + 'distance/v1/'; 26 | var Utils = { 27 | /** 28 | * 得到终点query字符串 29 | * @param {Array|String} 检索数据 30 | */ 31 | location2query(data) { 32 | if (typeof data == 'string') { 33 | return data; 34 | } 35 | var query = ''; 36 | for (var i = 0; i < data.length; i++) { 37 | var d = data[i]; 38 | if (!!query) { 39 | query += ';'; 40 | } 41 | if (d.location) { 42 | query = query + d.location.lat + ',' + d.location.lng; 43 | } 44 | if (d.latitude && d.longitude) { 45 | query = query + d.latitude + ',' + d.longitude; 46 | } 47 | } 48 | return query; 49 | }, 50 | 51 | /** 52 | * 使用微信接口进行定位 53 | */ 54 | getWXLocation(success, fail, complete) { 55 | wx.getLocation({ 56 | type: 'gcj02', 57 | success: success, 58 | fail: fail, 59 | complete: complete 60 | }); 61 | }, 62 | 63 | /** 64 | * 获取location参数 65 | */ 66 | getLocationParam(location) { 67 | if (typeof location == 'string') { 68 | var locationArr = location.split(','); 69 | if (locationArr.length === 2) { 70 | location = { 71 | latitude: location.split(',')[0], 72 | longitude: location.split(',')[1] 73 | }; 74 | } else { 75 | location = {}; 76 | } 77 | } 78 | return location; 79 | }, 80 | 81 | /** 82 | * 回调函数默认处理 83 | */ 84 | polyfillParam(param) { 85 | param.success = param.success || function () { }; 86 | param.fail = param.fail || function () { }; 87 | param.complete = param.complete || function () { }; 88 | }, 89 | 90 | /** 91 | * 验证param对应的key值是否为空 92 | * 93 | * @param {Object} param 接口参数 94 | * @param {String} key 对应参数的key 95 | */ 96 | checkParamKeyEmpty(param, key) { 97 | if (!param[key]) { 98 | var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + key +'参数格式有误'); 99 | param.fail(errconf); 100 | param.complete(errconf); 101 | return true; 102 | } 103 | return false; 104 | }, 105 | 106 | /** 107 | * 验证参数中是否存在检索词keyword 108 | * 109 | * @param {Object} param 接口参数 110 | */ 111 | checkKeyword(param){ 112 | return !this.checkParamKeyEmpty(param, 'keyword'); 113 | }, 114 | 115 | /** 116 | * 验证location值 117 | * 118 | * @param {Object} param 接口参数 119 | */ 120 | checkLocation(param) { 121 | var location = this.getLocationParam(param.location); 122 | if (!location || !location.latitude || !location.longitude) { 123 | var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' location参数格式有误') 124 | param.fail(errconf); 125 | param.complete(errconf); 126 | return false; 127 | } 128 | return true; 129 | }, 130 | 131 | /** 132 | * 构造错误数据结构 133 | * @param {Number} errCode 错误码 134 | * @param {Number} errMsg 错误描述 135 | */ 136 | buildErrorConfig(errCode, errMsg) { 137 | return { 138 | status: errCode, 139 | message: errMsg 140 | }; 141 | }, 142 | 143 | /** 144 | * 构造微信请求参数,公共属性处理 145 | * 146 | * @param {Object} param 接口参数 147 | * @param {Object} param 配置项 148 | */ 149 | buildWxRequestConfig(param, options) { 150 | var that = this; 151 | options.header = { "content-type": "application/json" }; 152 | options.method = 'GET'; 153 | options.success = function (res) { 154 | var data = res.data; 155 | if (data.status === 0) { 156 | param.success(data); 157 | } else { 158 | param.fail(data); 159 | } 160 | }; 161 | options.fail = function (res) { 162 | res.statusCode = ERROR_CONF.WX_ERR_CODE; 163 | param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, result.errMsg)); 164 | }; 165 | options.complete = function (res) { 166 | var statusCode = +res.statusCode; 167 | switch(statusCode) { 168 | case ERROR_CONF.WX_ERR_CODE: { 169 | param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)); 170 | break; 171 | } 172 | case ERROR_CONF.WX_OK_CODE: { 173 | var data = res.data; 174 | if (data.status === 0) { 175 | param.complete(data); 176 | } else { 177 | param.complete(that.buildErrorConfig(data.status, data.message)); 178 | } 179 | break; 180 | } 181 | default:{ 182 | param.complete(that.buildErrorConfig(ERROR_CONF.SYSTEM_ERR, ERROR_CONF.SYSTEM_ERR_MSG)); 183 | } 184 | 185 | } 186 | } 187 | return options; 188 | }, 189 | 190 | /** 191 | * 处理用户参数是否传入坐标进行不同的处理 192 | */ 193 | locationProcess(param, locationsuccess, locationfail, locationcomplete) { 194 | var that = this; 195 | locationfail = locationfail || function (res) { 196 | res.statusCode = ERROR_CONF.WX_ERR_CODE; 197 | param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)); 198 | }; 199 | locationcomplete = locationcomplete || function (res) { 200 | if (res.statusCode == ERROR_CONF.WX_ERR_CODE) { 201 | param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)); 202 | } 203 | }; 204 | if (!param.location) { 205 | that.getWXLocation(locationsuccess, locationfail, locationcomplete); 206 | } else if (that.checkLocation(param)) { 207 | var location = Utils.getLocationParam(param.location); 208 | locationsuccess(location); 209 | } 210 | } 211 | } 212 | 213 | 214 | class QQMapWX { 215 | 216 | /** 217 | * 构造函数 218 | * 219 | * @param {Object} options 接口参数,key 为必选参数 220 | */ 221 | constructor(options) { 222 | if (!options.key) { 223 | throw Error('key值不能为空'); 224 | } 225 | this.key = options.key; 226 | } 227 | 228 | /** 229 | * POI周边检索 230 | * 231 | * @param {Object} options 接口参数对象 232 | * 233 | * 参数对象结构可以参考 234 | * @see http://lbs.qq.com/webservice_v1/guide-search.html 235 | */ 236 | search(options) { 237 | var that = this; 238 | options = options || {}; 239 | 240 | Utils.polyfillParam(options); 241 | 242 | if (!Utils.checkKeyword(options)) { 243 | return; 244 | } 245 | 246 | var requestParam = { 247 | keyword: options.keyword, 248 | orderby: options.orderby || '_distance', 249 | page_size: options.page_size || 10, 250 | page_index: options.page_index || 1, 251 | output: 'json', 252 | key: that.key 253 | }; 254 | 255 | if (options.address_format) { 256 | requestParam.address_format = options.address_format; 257 | } 258 | 259 | if (options.filter) { 260 | requestParam.filter = options.filter; 261 | } 262 | 263 | var distance = options.distance || "1000"; 264 | var auto_extend = options.auto_extend || 1; 265 | 266 | var locationsuccess = function (result) { 267 | requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance + "," + auto_extend +")"; 268 | wx.request(Utils.buildWxRequestConfig(options, { 269 | url: URL_SEARCH, 270 | data: requestParam 271 | })); 272 | } 273 | Utils.locationProcess(options, locationsuccess); 274 | } 275 | 276 | /** 277 | * sug模糊检索 278 | * 279 | * @param {Object} options 接口参数对象 280 | * 281 | * 参数对象结构可以参考 282 | * http://lbs.qq.com/webservice_v1/guide-suggestion.html 283 | */ 284 | getSuggestion(options) { 285 | var that = this; 286 | options = options || {}; 287 | Utils.polyfillParam(options); 288 | 289 | if (!Utils.checkKeyword(options)) { 290 | return; 291 | } 292 | 293 | var requestParam = { 294 | keyword: options.keyword, 295 | region: options.region || '全国', 296 | region_fix: options.region_fix || 0, 297 | policy: options.policy || 0, 298 | output: 'json', 299 | key: that.key 300 | }; 301 | wx.request(Utils.buildWxRequestConfig(options, { 302 | url: URL_SUGGESTION, 303 | data: requestParam 304 | })); 305 | } 306 | 307 | /** 308 | * 逆地址解析 309 | * 310 | * @param {Object} options 接口参数对象 311 | * 312 | * 请求参数结构可以参考 313 | * http://lbs.qq.com/webservice_v1/guide-gcoder.html 314 | */ 315 | reverseGeocoder(options) { 316 | var that = this; 317 | options = options || {}; 318 | Utils.polyfillParam(options); 319 | var requestParam = { 320 | coord_type: options.coord_type || 5, 321 | get_poi: options.get_poi || 0, 322 | output: 'json', 323 | key: that.key 324 | }; 325 | if (options.poi_options) { 326 | requestParam.poi_options = options.poi_options 327 | } 328 | 329 | var locationsuccess = function (result) { 330 | requestParam.location = result.latitude + ',' + result.longitude; 331 | wx.request(Utils.buildWxRequestConfig(options, { 332 | url: URL_GET_GEOCODER, 333 | data: requestParam 334 | })); 335 | }; 336 | Utils.locationProcess(options, locationsuccess); 337 | } 338 | 339 | /** 340 | * 地址解析 341 | * 342 | * @param {Object} options 接口参数对象 343 | * 344 | * 请求参数结构可以参考 345 | * http://lbs.qq.com/webservice_v1/guide-geocoder.html 346 | */ 347 | geocoder(options) { 348 | var that = this; 349 | options = options || {}; 350 | Utils.polyfillParam(options); 351 | 352 | if (Utils.checkParamKeyEmpty(options, 'address')) { 353 | return; 354 | } 355 | 356 | var requestParam = { 357 | address: options.address, 358 | output: 'json', 359 | key: that.key 360 | }; 361 | 362 | wx.request(Utils.buildWxRequestConfig(options, { 363 | url: URL_GET_GEOCODER, 364 | data: requestParam 365 | })); 366 | } 367 | 368 | 369 | /** 370 | * 获取城市列表 371 | * 372 | * @param {Object} options 接口参数对象 373 | * 374 | * 请求参数结构可以参考 375 | * http://lbs.qq.com/webservice_v1/guide-region.html 376 | */ 377 | getCityList(options) { 378 | var that = this; 379 | options = options || {}; 380 | Utils.polyfillParam(options); 381 | var requestParam = { 382 | output: 'json', 383 | key: that.key 384 | }; 385 | 386 | wx.request(Utils.buildWxRequestConfig(options, { 387 | url: URL_CITY_LIST, 388 | data: requestParam 389 | })); 390 | } 391 | 392 | /** 393 | * 获取对应城市ID的区县列表 394 | * 395 | * @param {Object} options 接口参数对象 396 | * 397 | * 请求参数结构可以参考 398 | * http://lbs.qq.com/webservice_v1/guide-region.html 399 | */ 400 | getDistrictByCityId(options) { 401 | var that = this; 402 | options = options || {}; 403 | Utils.polyfillParam(options); 404 | 405 | if (Utils.checkParamKeyEmpty(options, 'id')) { 406 | return; 407 | } 408 | 409 | var requestParam = { 410 | id: options.id || '', 411 | output: 'json', 412 | key: that.key 413 | }; 414 | 415 | wx.request(Utils.buildWxRequestConfig(options, { 416 | url: URL_AREA_LIST, 417 | data: requestParam 418 | })); 419 | } 420 | 421 | /** 422 | * 用于单起点到多终点的路线距离(非直线距离)计算: 423 | * 支持两种距离计算方式:步行和驾车。 424 | * 起点到终点最大限制直线距离10公里。 425 | * 426 | * @param {Object} options 接口参数对象 427 | * 428 | * 请求参数结构可以参考 429 | * http://lbs.qq.com/webservice_v1/guide-distance.html 430 | */ 431 | calculateDistance(options) { 432 | var that = this; 433 | options = options || {}; 434 | Utils.polyfillParam(options); 435 | 436 | if (Utils.checkParamKeyEmpty(options, 'to')) { 437 | return; 438 | } 439 | 440 | var requestParam = { 441 | mode: options.mode || 'walking', 442 | to: Utils.location2query(options.to), 443 | output: 'json', 444 | key: that.key 445 | }; 446 | 447 | var locationsuccess = function (result) { 448 | requestParam.from = result.latitude + ',' + result.longitude; 449 | wx.request(Utils.buildWxRequestConfig(options, { 450 | url: URL_DISTANCE, 451 | data: requestParam 452 | })); 453 | } 454 | if (options.from) { 455 | options.location = options.from; 456 | } 457 | 458 | Utils.locationProcess(options, locationsuccess); 459 | } 460 | } 461 | 462 | module.exports = QQMapWX; -------------------------------------------------------------------------------- /libs/qqmap-wx-jssdk.min.js: -------------------------------------------------------------------------------- 1 | var _createClass=function(){function a(e,c){for(var b=0;b 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | {{address}} 13 | -------------------------------------------------------------------------------- /pages/checkin/checkin.wxss: -------------------------------------------------------------------------------- 1 | /* pages/checkin/checkin.wxss */ 2 | .checkbox-area{ 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: space-between; 6 | } 7 | .checkbox{ 8 | color: #888; 9 | align-items: center; 10 | justify-content: space-between; 11 | } 12 | .checkbox input{ 13 | background: #09bb07; 14 | border: 0 none; 15 | } 16 | /** 修改button默认的点击态样式类**/ 17 | .button-hover { 18 | background-color: yellow; 19 | opacity: 0.7; 20 | } 21 | /** 添加自定义button点击态样式类**/ 22 | .other-button-hover { 23 | background-color: green; 24 | opacity: 0.7; 25 | } 26 | .margin-button{ 27 | height: 40px; 28 | width: 100px; 29 | margin-top: 50px; 30 | } -------------------------------------------------------------------------------- /pages/detail/detail.js: -------------------------------------------------------------------------------- 1 | // pages/detail/detail.js 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | postId:0, 9 | postTitle:"", 10 | postContent:"", 11 | postRemarks:[], 12 | newPostRemark:"", 13 | userNumber:"" 14 | }, 15 | 16 | /** 17 | * 生命周期函数--监听页面加载 18 | */ 19 | onLoad: function (options) { 20 | console.log(options.id); 21 | var that=this; 22 | that.setData( 23 | {postId:options.id} 24 | ); 25 | console.log(that.data.postId); 26 | wx.request({ 27 | url: 'http://localhost:8080/WeChat/GetPostDetail', 28 | data: { postId: that.data.postId }, 29 | method: 'GET', 30 | header: { 31 | 'content-type': 'application/json;charset=utf-8' // 默认值 32 | }, 33 | 34 | success: function (res) { 35 | console.log('submit success'); 36 | console.log(res.data); 37 | console.log(res); 38 | 39 | var remarks=[]; 40 | for(var i=0;i 2 | 帖子内容 3 | {{postContent}} 4 | 已有评论 5 | 6 | 7 | 8 | {{index+1}}.{{item.value}} 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /pages/detail/detail.wxss: -------------------------------------------------------------------------------- 1 | /* pages/detail/detail.wxss */ 2 | .log-list { 3 | display: flex; 4 | flex-direction: column; 5 | padding: 40rpx; 6 | } 7 | .log-item { 8 | margin: 10rpx; 9 | } 10 | .title{ 11 | width: 320px; 12 | height: 50px; 13 | border: 1px solid; 14 | margin: 10px 20px; 15 | } 16 | .margin-button{ 17 | height: 40px; 18 | width: 200px; 19 | margin-top: 50px; 20 | } -------------------------------------------------------------------------------- /pages/home/home.js: -------------------------------------------------------------------------------- 1 | // pages/home/home.js 2 | const app = getApp() 3 | 4 | Page({ 5 | /** 6 | * 页面的初始数据 7 | */ 8 | data: { 9 | userInfo: {}, 10 | hasUserInfo: false, 11 | canIUse: wx.canIUse('button.open-type.getUserInfo'), 12 | userBindInfo: [], 13 | hasUserBindInfo:false 14 | }, 15 | 16 | bindViewTap: function () { 17 | wx.navigateTo({ 18 | url: '../logs/logs' 19 | }) 20 | }, 21 | 22 | /** 23 | * 生命周期函数--监听页面加载 24 | */ 25 | onLoad: function () { 26 | var that=this; 27 | if (app.globalData.userInfo) { 28 | //console.log("begingebin"); 29 | //console.log(app.globalData.userInfo) 30 | this.setData({ 31 | userInfo: app.globalData.userInfo, 32 | hasUserInfo: true 33 | }) 34 | } else if (this.data.canIUse) { 35 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 36 | // 所以此处加入 callback 以防止这种情况 37 | app.userInfoReadyCallback = res => { 38 | this.setData({ 39 | userInfo: res.userInfo, 40 | hasUserInfo: true 41 | }) 42 | } 43 | } else { 44 | // 在没有 open-type=getUserInfo 版本的兼容处理 45 | wx.getUserInfo({ 46 | success: res => { 47 | app.globalData.userInfo = res.userInfo 48 | this.setData({ 49 | userInfo: res.userInfo, 50 | hasUserInfo: true 51 | }) 52 | } 53 | }) 54 | } 55 | if (app.globalData.userBindInfo) { 56 | that.setData({ 57 | userBindInfo: app.globalData.userBindInfo, 58 | hasUserBindInfo: true 59 | }) 60 | } 61 | else{ 62 | wx.showToast({ 63 | title: '请绑定信息', 64 | icon:"loading", 65 | duration: 2000 66 | }) 67 | } 68 | //console.log("finalfinal"); 69 | //console.log(that.data.userBindInfo[0].value); 70 | }, 71 | //获取用户信息 72 | getUserInfo: function (e) { 73 | console.log(e) 74 | app.globalData.userInfo = e.detail.userInfo 75 | that.setData({ 76 | userInfo: e.detail.userInfo, 77 | hasUserInfo: true 78 | }) 79 | }, 80 | postRequst: function () { 81 | wx.navigateTo({ 82 | url: '../postRequest/postRequest' 83 | }) 84 | }, 85 | remarkRequest:function(){ 86 | wx.navigateTo({ 87 | url: '../remarkRequest/remarkRequest' 88 | }) 89 | }, 90 | onShow: function () { 91 | if (!(app.globalData.userBindInfo)) { 92 | wx.showToast({ 93 | title: '请绑定信息', 94 | icon: "loading", 95 | duration: 2000 96 | }) 97 | } 98 | } 99 | }) -------------------------------------------------------------------------------- /pages/home/home.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /pages/home/home.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{userInfo.nickName}} 5 | {{"学号:"+userBindInfo[0].value}} 6 | {{"姓名:"+userBindInfo[1].value}} 7 | {{"任课老师:"+userBindInfo[2].value}} 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /pages/home/home.wxss: -------------------------------------------------------------------------------- 1 | /* pages/home/home.wxss */ 2 | .userinfo { 3 | display: flex; 4 | flex-direction: column; 5 | align-items: center; 6 | } 7 | 8 | .userinfo-avatar { 9 | width: 128rpx; 10 | height: 128rpx; 11 | margin: 20rpx; 12 | border-radius: 50%; 13 | } 14 | 15 | .userinfo-nickname { 16 | color: #aaa; 17 | } 18 | 19 | .margin-button{ 20 | height: 40px; 21 | width: 200px; 22 | margin-top: 50px; 23 | } -------------------------------------------------------------------------------- /pages/index/index.js: -------------------------------------------------------------------------------- 1 | //index.js 2 | //获取应用实例 3 | const app = getApp() 4 | 5 | Page({ 6 | data: { 7 | userInfo: {}, 8 | hasUserInfo: false, 9 | canIUse: wx.canIUse('button.open-type.getUserInfo'), 10 | userBindInfo:[], 11 | hasUserBindInfo:false, 12 | userName: '', 13 | userNumber: '', 14 | userTeacher: '', 15 | tabTxt: [ 16 | { 17 | 'text': '点击选择老师', 18 | 'originalText': '点击选择老师', 19 | 'active': false, 20 | 'child': [ 21 | { 'id': 1, 'text': '贾老师' }, 22 | { 'id': 2, 'text': '张老师' }, 23 | { 'id': 3, 'text': '李老师' } 24 | ], 25 | 'type': 0 26 | } 27 | ], 28 | searchParam: [] 29 | }, 30 | //事件处理函数 31 | bindViewTap: function() { 32 | wx.navigateTo({ 33 | url: '../logs/logs' 34 | }) 35 | }, 36 | onLoad: function () { 37 | //console.log("onload onload"); 38 | if (app.globalData.userInfo) { 39 | this.setData({ 40 | userInfo: app.globalData.userInfo, 41 | hasUserInfo: true 42 | }) 43 | } else if (this.data.canIUse){ 44 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 45 | // 所以此处加入 callback 以防止这种情况 46 | app.userInfoReadyCallback = res => { 47 | this.setData({ 48 | userInfo: res.userInfo, 49 | hasUserInfo: true 50 | }) 51 | } 52 | } else { 53 | // 在没有 open-type=getUserInfo 版本的兼容处理 54 | wx.getUserInfo({ 55 | success: res => { 56 | app.globalData.userInfo = res.userInfo 57 | this.setData({ 58 | userInfo: res.userInfo, 59 | hasUserInfo: true 60 | }) 61 | } 62 | }) 63 | } 64 | //console.log("nullnullnullnullllllllllllllllllllllll") 65 | if (app.globalData.userBindInfo) { 66 | var that=this; 67 | //console.log("not nullllllllllllllllllllllll") 68 | console.log("app.globalData.userBindInfo" + app.globalData.userBindInfo); 69 | that.setData({ 70 | userBindInfo: app.globalData.userBindInfo, 71 | hasUserBindInfo: true 72 | }) 73 | } 74 | }, 75 | //获取用户信息 76 | getUserInfo: function(e) { 77 | console.log(e) 78 | app.globalData.userInfo = e.detail.userInfo 79 | this.setData({ 80 | userInfo: e.detail.userInfo, 81 | hasUserInfo: true 82 | }) 83 | }, 84 | getUserName:function(e){ 85 | var that=this; 86 | this.setData({ 87 | userName: e.detail.value 88 | }) 89 | //console.log("userName:" + that.data.userName) 90 | }, 91 | getUserNumber: function (e) { 92 | var that=this; 93 | this.setData({ 94 | userNumber: e.detail.value 95 | }) 96 | //console.log("userNumber:" + that.data.userNumber) 97 | }, 98 | //下拉表单:与一级按钮绑定事件 99 | filterTab: function (e) { 100 | var that = this; 101 | var data = JSON.parse(JSON.stringify(that.data.tabTxt)); 102 | var index = e.currentTarget.dataset.index; 103 | var newTabTxt = data.map(function (e) { 104 | e.active = false; 105 | return e; 106 | }); 107 | newTabTxt[index].active = !that.data.tabTxt[index].active; 108 | this.setData({ 109 | tabTxt: data 110 | }) 111 | 112 | }, 113 | //下拉表单:与二级目录绑定事件 114 | filterTabChild: function (e) { 115 | //我需要切换选中项 修改展示文字 并收回抽屉 116 | var that = this; 117 | var index = e.currentTarget.dataset.index; 118 | var data = JSON.parse(JSON.stringify(that.data.tabTxt)); 119 | if (typeof (e.target.dataset.id) == 'undefined' || e.target.dataset.id == '') { 120 | data[index].active = !that.data.tabTxt[index].active; 121 | } 122 | else { 123 | data[index].type = e.target.dataset.id; 124 | data[index].active = !that.data.tabTxt[index].active; 125 | if (e.target.dataset.id == '0') { 126 | data[index].text = that.data.tabTxt[index].originalText; 127 | //不限删除条件 128 | delete that.data.searchParam[index]; 129 | } 130 | else { 131 | data[index].text = e.target.dataset.txt; 132 | //更改删除条件 133 | that.data.searchParam[index] = data[index].text; 134 | } 135 | } 136 | that.setData({ 137 | tabTxt: data 138 | }) 139 | //console.log(that.data.searchParam); 140 | 141 | that.setData({userTeacher:that.data.searchParam}); 142 | //that.setData({ userName: that.data.searchParam }); 143 | //that.setData({ userNumber: that.data.searchParam }); 144 | //console.log("userName:" + that.data.userName); 145 | //console.log("userNumber:" + that.data.userNumber); 146 | //console.log("userTeacher:" + that.data.userTeacher); 147 | 148 | var obj1 = {}; 149 | obj1.title = "userName"; 150 | obj1.value = that.data.userName; 151 | var obj2 = {}; 152 | obj2.title = "userNumber"; 153 | obj2.value = that.data.userNumber; 154 | var obj3 = {}; 155 | obj3.title = "userTeacher"; 156 | obj3.value = that.data.userTeacher[0]; 157 | //console.log(a); 158 | let userBindInfo = that.data.userBindInfo; 159 | userBindInfo.push(obj1); 160 | userBindInfo.push(obj2); 161 | userBindInfo.push(obj3); 162 | 163 | //console.log(that.data.userBindInfo); 164 | app.globalData.userBindInfo = that.data.userBindInfo; 165 | //console.log(app.globalData.userBindInfo); 166 | }, 167 | 168 | //绑定信息函数 169 | bind:function(e){ 170 | console.log("something"); 171 | var that = this; 172 | console.log(that.data.userBindInfo); 173 | var Util= require('../../utils/util.js'); 174 | wx.request({ 175 | url: 'http://localhost:8080/WeChat/Bind', 176 | data: Util.json2Form({ 177 | userNumber: that.data.userNumber, 178 | userName: that.data.userName, 179 | teacherName: that.data.userTeacher[0] 180 | }), 181 | method: 'POST', 182 | header: { 183 | "Content-Type": 'application/x-www-form-urlencoded;charset=utf-8', 184 | }, 185 | complete: function (res) { 186 | console.log("hhhhhhhhhhhhhhhhhhhh") 187 | wx.showToast({ 188 | title: '成功', 189 | icon: 'succes', 190 | duration: 1000, 191 | mask: true 192 | }) 193 | that.setData({ 194 | hasUserBindInfo:true 195 | }) 196 | app.globalData.userBindInfo=that.data.userBindInfo; 197 | that.setData({ 198 | userBindInfo: app.globalData.userBindInfo 199 | }) 200 | if (res == null || res.data == null) { 201 | console.error('网络请求失败'); 202 | return; 203 | } 204 | } 205 | }) 206 | } 207 | }) 208 | -------------------------------------------------------------------------------- /pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{userInfo.nickName}} 8 | 9 | 10 | 姓名: 11 | 12 | 13 | 14 | 学号: 15 | 16 | 17 | 18 | 任课老师: 19 | 20 | 21 | {{item.text}} 22 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | .userinfo { 3 | display: flex; 4 | flex-direction: column; 5 | align-items: center; 6 | } 7 | 8 | .userinfo-avatar { 9 | width: 128rpx; 10 | height: 128rpx; 11 | margin: 20rpx; 12 | border-radius: 50%; 13 | } 14 | .userinfo-nickname { 15 | color: #aaa; 16 | } 17 | 18 | .cxj-menu { 19 | background-color: #f5f5f5; 20 | width: 750rpx; 21 | height: 80rpx; 22 | display: flex; 23 | flex-wrap: nowrap; 24 | font-size: 32rpx; 25 | align-items: center; 26 | } 27 | .cxj-menu .active image { 28 | transform: rotate(180deg); 29 | background: none; 30 | } 31 | .cxj-menu .active { 32 | background-color: #fff; 33 | } 34 | 35 | .flex-view { 36 | -webkit-box-flex: 1; 37 | -webkit-flex: 1; 38 | -ms-flex: 1; 39 | flex: 1; 40 | overflow: hidden; 41 | display: block; 42 | text-align: center; 43 | line-height: 80rpx; 44 | } 45 | 46 | .cxj-tab-layout { 47 | width: 750rpx; 48 | overflow: hidden; 49 | position: fixed; 50 | top: 480rpx; 51 | z-index: 1; 52 | border-bottom: solid 1px #eee; 53 | height: 100%; 54 | background-color: rgba(000,000,000,.5); 55 | 56 | } 57 | .cxj-tab-layout .active { 58 | color: #00aa14; 59 | } 60 | 61 | .cxj-tab-layout view { 62 | width: 750rpx; 63 | text-indent: 40rpx; 64 | height: 80rpx; 65 | line-height: 80rpx; 66 | border-top: 1rpx solid #e7e7e7; 67 | background-color:#ffffff; 68 | } 69 | 70 | .margin-button{ 71 | height: 40px; 72 | width: 100px; 73 | margin-top: 50px; 74 | } -------------------------------------------------------------------------------- /pages/interactive/interactive.js: -------------------------------------------------------------------------------- 1 | // pages/interactive/interactive.js 2 | /*var WxParse = require('../../utils/wxParse/wxParse.js'); 3 | const content1 = require("../../content/1content.js"); 4 | const content2 = require("../../content/2content.js"); 5 | const content3 = require("../../content/3content.js"); 6 | const content4 = require("../../content/4content.js"); 7 | const content5 = require("../../content/5content.js"); 8 | //暂时还没有形成数组*/ 9 | const app = getApp() 10 | 11 | Page({ 12 | 13 | /** 14 | * 页面的初始数据 15 | */ 16 | data: { 17 | //posts: [] 18 | item:[], 19 | postId:1 20 | //image_covers: ['img1.jpg', 'img2.jpg', 'img3.jpg', 'img4.jpg','img5.jpg'] 21 | }, 22 | 23 | getDetail:function(e){ 24 | var that=this 25 | //console.log(e.currentTarget.id); 26 | that.setData({ postId: e.currentTarget.id}); 27 | //console.log(that.data.postId); 28 | var Id = String(that.data.postId) 29 | wx.navigateTo({ 30 | url: '../detail/detail?id=' + Id 31 | }) 32 | }, 33 | /** 34 | * 生命周期函数--监听页面加载 35 | */ 36 | onLoad: function (options) { 37 | if (!(app.globalData.userBindInfo)) { 38 | wx.showToast({ 39 | title: '请绑定信息', 40 | icon: "loading", 41 | duration: 2000 42 | }) 43 | } 44 | //var that=this; 45 | /*var data = [{ content:content1.content}, 46 | {content: content2.content }, 47 | {content: content3.content }, 48 | {content: content4.content }, 49 | {content: content5.content }] 50 | console.log(data); 51 | this.setData({ 52 | item:data 53 | }) 54 | var content=new Array(); 55 | //content.push("content"+String(4));///okokok 56 | 57 | for(var i=0;i<5;i++){ 58 | var target="content"+String(i+1); 59 | //console.log(target); 60 | content.push(target); 61 | WxParse.wxParse(content[i],'html',data[i].content,that,25); 62 | } 63 | console.log(content); 64 | 65 | //console.log(wxParseData) 66 | //WxParse.wxParse(content[1], 'html', data[1].content, that, 25), 67 | //WxParse.wxParse(content[2], 'html', data[2].content, that, 25); 68 | //WxParse.wxParse(content[3], 'html', data[2].content, that, 25); 69 | //content.push(content1); 70 | //content.push(content2); 71 | //content.push(content3); 72 | 73 | //console.log(content[0]) 74 | */ 75 | var that = this; 76 | wx.request({ 77 | url: 'http://localhost:8080/WeChat/GetAllPosts', 78 | method: 'GET', 79 | header: { 80 | 'content-type': 'application/json;charset=utf-8' // 默认值 81 | }, 82 | 83 | success: function (res) { 84 | console.log('submit success'); 85 | console.log(res.data); 86 | console.log(res); 87 | 88 | that.setData({ 89 | posts: res.data 90 | }) 91 | console.log(that.data.posts) 92 | 93 | }, 94 | fail: function (res) { 95 | console.log('submit fail'); 96 | }, 97 | complete: function (res) { 98 | console.log('submit complete'); 99 | } 100 | 101 | }) 102 | }, 103 | 104 | /** 105 | * 生命周期函数--监听页面初次渲染完成 106 | */ 107 | onReady: function () { 108 | 109 | }, 110 | 111 | /** 112 | * 生命周期函数--监听页面显示 113 | */ 114 | onShow: function () { 115 | if (!(app.globalData.userBindInfo)) { 116 | wx.showToast({ 117 | title: '请绑定信息', 118 | icon: "loading", 119 | duration: 2000 120 | }) 121 | } 122 | }, 123 | 124 | /** 125 | * 生命周期函数--监听页面隐藏 126 | */ 127 | onHide: function () { 128 | 129 | }, 130 | 131 | /** 132 | * 生命周期函数--监听页面卸载 133 | */ 134 | onUnload: function () { 135 | 136 | }, 137 | 138 | /** 139 | * 页面相关事件处理函数--监听用户下拉动作 140 | */ 141 | onPullDownRefresh: function () { 142 | 143 | }, 144 | 145 | /** 146 | * 页面上拉触底事件的处理函数 147 | */ 148 | onReachBottom: function () { 149 | 150 | }, 151 | 152 | /** 153 | * 用户点击右上角分享 154 | */ 155 | onShareAppMessage: function () { 156 | 157 | } 158 | }) -------------------------------------------------------------------------------- /pages/interactive/interactive.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /pages/interactive/interactive.wxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 点击文章标题查看详细内容 9 | 10 | 11 | 12 | {{index+1}}. 帖子标题:{{item.postTitle}} 13 | 帖子作者:{{item.userNumber}} 14 | 15 | 16 | 17 | 21 | 22 | 40 | 41 | 42 | 44 | -------------------------------------------------------------------------------- /pages/interactive/interactive.wxss: -------------------------------------------------------------------------------- 1 | /* pages/interactive/interactive.wxss */ 2 | 3 | .log-list { 4 | display: flex; 5 | flex-direction: column; 6 | padding: 40rpx; 7 | } 8 | .log-item { 9 | margin: 10rpx; 10 | } 11 | 12 | .post{ 13 | width:310px; 14 | } 15 | 16 | .post_container { 17 | display: flex; 18 | flex-direction: column; 19 | align-items: center; 20 | justify-content: space-between; 21 | box-sizing: border-box; 22 | } 23 | .navigate{ 24 | width: 100%; 25 | display: flex; 26 | justify-content: center; 27 | } -------------------------------------------------------------------------------- /pages/logs/logs.js: -------------------------------------------------------------------------------- 1 | //logs.js 2 | const util = require('../../utils/util.js') 3 | 4 | Page({ 5 | data: { 6 | logs: [] 7 | }, 8 | onLoad: function () { 9 | this.setData({ 10 | logs: (wx.getStorageSync('logs') || []).map(log => { 11 | return util.formatTime(new Date(log)) 12 | }) 13 | }) 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /pages/logs/logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "查看启动日志" 3 | } -------------------------------------------------------------------------------- /pages/logs/logs.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{index+1}}. {{log}} 5 | 6 | 7 | -------------------------------------------------------------------------------- /pages/logs/logs.wxss: -------------------------------------------------------------------------------- 1 | .log-list { 2 | display: flex; 3 | flex-direction: column; 4 | padding: 40rpx; 5 | } 6 | .log-item { 7 | margin: 10rpx; 8 | } 9 | -------------------------------------------------------------------------------- /pages/postRequest/postRequest.js: -------------------------------------------------------------------------------- 1 | // pages/postRequest/postRequest.js 2 | //获取应用实例 3 | const app = getApp() 4 | 5 | 6 | Page({ 7 | /** 8 | * 页面的初始数据 9 | */ 10 | data: { 11 | userBindInfo:[], 12 | postTitle:"", 13 | PostContent:"" 14 | }, 15 | 16 | putPost:function(){ 17 | console.log("something"); 18 | var that = this; 19 | console.log(that.data.userBindInfo); 20 | var Util = require('../../utils/util.js'); 21 | wx.request({ 22 | url: 'http://localhost:8080/WeChat/PutPost', 23 | data: Util.json2Form({ 24 | userNumber: that.data.userBindInfo[1].value, 25 | //userName: that.data.userName, 26 | //teacherName: that.data.userTeacher[0], 27 | postTitle: that.data.postTitle, 28 | postContent: that.data.postContent 29 | }), 30 | method: 'POST', 31 | header: { 32 | "Content-Type": 'application/x-www-form-urlencoded;charset=utf-8', 33 | }, 34 | complete: function (res) { 35 | console.log("hhhhhhhhhhhhhhhhhhhh") 36 | wx.showToast({ 37 | title: '成功', 38 | icon: 'succes', 39 | duration: 1000, 40 | mask: true 41 | }) 42 | that.data.hasUserBindInfo = true; 43 | app.globalData.userBindInfo = that.data.userBindInfo; 44 | 45 | if (res == null || res.data == null) { 46 | console.error('网络请求失败'); 47 | return; 48 | } 49 | } 50 | }) 51 | }, 52 | getPostTitle: function (e) { 53 | var that = this; 54 | this.setData({ 55 | postTitle: e.detail.value 56 | }) 57 | console.log("postTitle:" + that.data.postTitle) 58 | }, 59 | getPostContent: function(e) { 60 | var that = this; 61 | this.setData({ 62 | postContent: e.detail.value 63 | }) 64 | console.log("getPostContent:" + that.data.postContent) 65 | }, 66 | 67 | /** 68 | * 生命周期函数--监听页面加载 69 | */ 70 | onLoad: function (options) { 71 | if (app.globalData.userBindInfo) { 72 | console.log("not nullllllllllllllllllllllll") 73 | console.log(app.globalData.userBindInfo); 74 | this.setData({ 75 | userBindInfo: app.globalData.userBindInfo, 76 | hasUserBindInfo: true 77 | }) 78 | } 79 | }, 80 | 81 | /** 82 | * 生命周期函数--监听页面初次渲染完成 83 | */ 84 | onReady: function () { 85 | 86 | }, 87 | 88 | /** 89 | * 生命周期函数--监听页面显示 90 | */ 91 | onShow: function () { 92 | 93 | }, 94 | 95 | /** 96 | * 生命周期函数--监听页面隐藏 97 | */ 98 | onHide: function () { 99 | 100 | }, 101 | 102 | /** 103 | * 生命周期函数--监听页面卸载 104 | */ 105 | onUnload: function () { 106 | 107 | }, 108 | 109 | /** 110 | * 页面相关事件处理函数--监听用户下拉动作 111 | */ 112 | onPullDownRefresh: function () { 113 | 114 | }, 115 | 116 | /** 117 | * 页面上拉触底事件的处理函数 118 | */ 119 | onReachBottom: function () { 120 | 121 | }, 122 | 123 | /** 124 | * 用户点击右上角分享 125 | */ 126 | onShareAppMessage: function () { 127 | 128 | } 129 | }) -------------------------------------------------------------------------------- /pages/postRequest/postRequest.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /pages/postRequest/postRequest.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /pages/postRequest/postRequest.wxss: -------------------------------------------------------------------------------- 1 | /* pages/postRequest/postRequest.wxss */ 2 | .title{ 3 | width: 320px; 4 | height: 50px; 5 | border: 1px solid; 6 | margin: 10px 20px; 7 | } 8 | .content{ 9 | width: 320px; 10 | height: 300px; 11 | border: 1px solid; 12 | margin: 10px 20px; 13 | } 14 | .margin-button{ 15 | height: 40px; 16 | width: 200px; 17 | margin-top: 50px; 18 | } -------------------------------------------------------------------------------- /pages/remarkRequest/remarkRequest.js: -------------------------------------------------------------------------------- 1 | // pages/remarkRequest/remarkRequest.js 2 | const app = getApp() 3 | 4 | Page({ 5 | /** 6 | * 页面的初始数据 7 | */ 8 | data: { 9 | remarks:[] 10 | }, 11 | 12 | /** 13 | * 生命周期函数--监听页面加载 14 | */ 15 | //直接在这里查询已有的评论 16 | 17 | onLoad: function (options) { 18 | var that=this; 19 | wx.request({ 20 | url: 'http://localhost:8080/WeChat/GetRemarks', 21 | data: { userNumber:app.globalData.userBindInfo[1].value}, 22 | method: 'GET', 23 | header: { 24 | 'content-type': 'application/json' // 默认值 25 | }, 26 | 27 | success: function (res) { 28 | console.log('submit success'); 29 | console.log(res.data); 30 | console.log(res); 31 | that.setData({ 32 | remarks: res.data 33 | }) 34 | console.log(that.data.remarks) 35 | 36 | }, 37 | fail: function (res) { 38 | console.log('submit fail'); 39 | }, 40 | complete: function (res) { 41 | console.log('submit complete'); 42 | } 43 | 44 | }) 45 | }, 46 | 47 | /** 48 | * 生命周期函数--监听页面初次渲染完成 49 | */ 50 | onReady: function () { 51 | 52 | }, 53 | 54 | /** 55 | * 生命周期函数--监听页面显示 56 | */ 57 | onShow: function () { 58 | 59 | }, 60 | 61 | /** 62 | * 生命周期函数--监听页面隐藏 63 | */ 64 | onHide: function () { 65 | 66 | }, 67 | 68 | /** 69 | * 生命周期函数--监听页面卸载 70 | */ 71 | onUnload: function () { 72 | 73 | }, 74 | 75 | /** 76 | * 页面相关事件处理函数--监听用户下拉动作 77 | */ 78 | onPullDownRefresh: function () { 79 | 80 | }, 81 | 82 | /** 83 | * 页面上拉触底事件的处理函数 84 | */ 85 | onReachBottom: function () { 86 | 87 | }, 88 | 89 | /** 90 | * 用户点击右上角分享 91 | */ 92 | onShareAppMessage: function () { 93 | 94 | } 95 | }) -------------------------------------------------------------------------------- /pages/remarkRequest/remarkRequest.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /pages/remarkRequest/remarkRequest.wxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | {{index+1}}. 帖子内容:{{item.postContent}} 10 | 帖子评论:{{item.postRemark}} 11 | 12 | -------------------------------------------------------------------------------- /pages/remarkRequest/remarkRequest.wxss: -------------------------------------------------------------------------------- 1 | /* pages/remarkRequest/remarkRequest.wxss */ 2 | .log-list { 3 | display: flex; 4 | flex-direction: column; 5 | padding: 40rpx; 6 | } 7 | .log-item { 8 | margin: 10rpx; 9 | } -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目配置文件。", 3 | "packOptions": { 4 | "ignore": [] 5 | }, 6 | "setting": { 7 | "urlCheck": false, 8 | "es6": true, 9 | "postcss": true, 10 | "minified": true, 11 | "newFeature": true 12 | }, 13 | "compileType": "miniprogram", 14 | "libVersion": "1.9.98", 15 | "appid": "wx4854780ce920b3ee", 16 | "projectname": "C%E8%AF%AD%E8%A8%80%E4%BA%92%E5%8A%A8%E8%AF%BE%E5%A0%82", 17 | "isGameTourist": false, 18 | "condition": { 19 | "search": { 20 | "current": -1, 21 | "list": [] 22 | }, 23 | "conversation": { 24 | "current": -1, 25 | "list": [] 26 | }, 27 | "plugin": { 28 | "current": -1, 29 | "list": [] 30 | }, 31 | "game": { 32 | "currentL": -1, 33 | "list": [] 34 | }, 35 | "miniprogram": { 36 | "current": -1, 37 | "list": [] 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /utils/util.js: -------------------------------------------------------------------------------- 1 | const formatTime = date => { 2 | const year = date.getFullYear() 3 | const month = date.getMonth() + 1 4 | const day = date.getDate() 5 | const hour = date.getHours() 6 | const minute = date.getMinutes() 7 | const second = date.getSeconds() 8 | 9 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') 10 | } 11 | 12 | const formatNumber = n => { 13 | n = n.toString() 14 | return n[1] ? n : '0' + n 15 | } 16 | 17 | module.exports = { 18 | formatTime: formatTime 19 | } 20 | 21 | function json2Form(json) { 22 | var str = []; 23 | for (var p in json) { 24 | str.push(encodeURIComponent(p) + "=" + encodeURIComponent(json[p])); 25 | } 26 | return str.join("&"); 27 | } 28 | module.exports = { 29 | json2Form: json2Form, 30 | } -------------------------------------------------------------------------------- /utils/wxParse/html2json.js: -------------------------------------------------------------------------------- 1 | /** 2 | * html2Json 改造来自: https://github.com/Jxck/html2json 3 | * 4 | * 5 | * author: Di (微信小程序开发工程师) 6 | * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) 7 | * 垂直微信小程序开发交流社区 8 | * 9 | * github地址: https://github.com/icindy/wxParse 10 | * 11 | * for: 微信小程序富文本解析 12 | * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 13 | */ 14 | 15 | var __placeImgeUrlHttps = "https"; 16 | var __emojisReg = ''; 17 | var __emojisBaseSrc = ''; 18 | var __emojis = {}; 19 | var wxDiscode = require('./wxDiscode.js'); 20 | var HTMLParser = require('./htmlparser.js'); 21 | // Empty Elements - HTML 5 22 | var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr"); 23 | // Block Elements - HTML 5 24 | 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"); 25 | 26 | // Inline Elements - HTML 5 27 | 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"); 28 | 29 | // Elements that you can, intentionally, leave open 30 | // (and which close themselves) 31 | var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr"); 32 | 33 | // Attributes that have their values filled in disabled="disabled" 34 | var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"); 35 | 36 | // Special Elements (can contain anything) 37 | var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block"); 38 | function makeMap(str) { 39 | var obj = {}, items = str.split(","); 40 | for (var i = 0; i < items.length; i++) 41 | obj[items[i]] = true; 42 | return obj; 43 | } 44 | 45 | function q(v) { 46 | return '"' + v + '"'; 47 | } 48 | 49 | function removeDOCTYPE(html) { 50 | return html 51 | .replace(/<\?xml.*\?>\n/, '') 52 | .replace(/<.*!doctype.*\>\n/, '') 53 | .replace(/<.*!DOCTYPE.*\>\n/, ''); 54 | } 55 | 56 | function trimHtml(html) { 57 | return html 58 | .replace(/\r?\n+/g, '') 59 | .replace(//ig, '') 60 | .replace(/\/\*.*?\*\//ig, '') 61 | .replace(/[ ]+ 189 | // add to parents 190 | var parent = bufArray[0] || results; 191 | if (parent.nodes === undefined) { 192 | parent.nodes = []; 193 | } 194 | parent.nodes.push(node); 195 | } else { 196 | bufArray.unshift(node); 197 | } 198 | }, 199 | end: function (tag) { 200 | //debug(tag); 201 | // merge into parent tag 202 | var node = bufArray.shift(); 203 | if (node.tag !== tag) console.error('invalid state: mismatch end tag'); 204 | 205 | //当有缓存source资源时于于video补上src资源 206 | if(node.tag === 'video' && results.source){ 207 | node.attr.src = results.source; 208 | delete results.source; 209 | } 210 | 211 | if (bufArray.length === 0) { 212 | results.nodes.push(node); 213 | } else { 214 | var parent = bufArray[0]; 215 | if (parent.nodes === undefined) { 216 | parent.nodes = []; 217 | } 218 | parent.nodes.push(node); 219 | } 220 | }, 221 | chars: function (text) { 222 | //debug(text); 223 | var node = { 224 | node: 'text', 225 | text: text, 226 | textArray:transEmojiStr(text) 227 | }; 228 | 229 | if (bufArray.length === 0) { 230 | node.index = index.toString() 231 | index += 1 232 | results.nodes.push(node); 233 | } else { 234 | var parent = bufArray[0]; 235 | if (parent.nodes === undefined) { 236 | parent.nodes = []; 237 | } 238 | node.index = parent.index + '.' + parent.nodes.length 239 | parent.nodes.push(node); 240 | } 241 | }, 242 | comment: function (text) { 243 | //debug(text); 244 | // var node = { 245 | // node: 'comment', 246 | // text: text, 247 | // }; 248 | // var parent = bufArray[0]; 249 | // if (parent.nodes === undefined) { 250 | // parent.nodes = []; 251 | // } 252 | // parent.nodes.push(node); 253 | }, 254 | }); 255 | return results; 256 | }; 257 | 258 | function transEmojiStr(str){ 259 | // var eReg = new RegExp("["+__reg+' '+"]"); 260 | // str = str.replace(/\[([^\[\]]+)\]/g,':$1:') 261 | 262 | var emojiObjs = []; 263 | //如果正则表达式为空 264 | if(__emojisReg.length == 0 || !__emojis){ 265 | var emojiObj = {} 266 | emojiObj.node = "text"; 267 | emojiObj.text = str; 268 | array = [emojiObj]; 269 | return array; 270 | } 271 | //这个地方需要调整 272 | str = str.replace(/\[([^\[\]]+)\]/g,':$1:') 273 | var eReg = new RegExp("[:]"); 274 | var array = str.split(eReg); 275 | for(var i = 0; i < array.length; i++){ 276 | var ele = array[i]; 277 | var emojiObj = {}; 278 | if(__emojis[ele]){ 279 | emojiObj.node = "element"; 280 | emojiObj.tag = "emoji"; 281 | emojiObj.text = __emojis[ele]; 282 | emojiObj.baseSrc= __emojisBaseSrc; 283 | }else{ 284 | emojiObj.node = "text"; 285 | emojiObj.text = ele; 286 | } 287 | emojiObjs.push(emojiObj); 288 | } 289 | 290 | return emojiObjs; 291 | } 292 | 293 | function emojisInit(reg='',baseSrc="/wxParse/emojis/",emojis){ 294 | __emojisReg = reg; 295 | __emojisBaseSrc=baseSrc; 296 | __emojis=emojis; 297 | } 298 | 299 | module.exports = { 300 | html2json: html2json, 301 | emojisInit:emojisInit 302 | }; 303 | 304 | -------------------------------------------------------------------------------- /utils/wxParse/htmlparser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * htmlParser改造自: https://github.com/blowsie/Pure-JavaScript-HTML5-Parser 4 | * 5 | * author: Di (微信小程序开发工程师) 6 | * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) 7 | * 垂直微信小程序开发交流社区 8 | * 9 | * github地址: https://github.com/icindy/wxParse 10 | * 11 | * for: 微信小程序富文本解析 12 | * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 13 | */ 14 | // Regular Expressions for parsing tags and attributes 15 | var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, 16 | endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/, 17 | attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; 18 | 19 | // Empty Elements - HTML 5 20 | var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr"); 21 | 22 | // Block Elements - HTML 5 23 | 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"); 24 | 25 | // Inline Elements - HTML 5 26 | 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"); 27 | 28 | // Elements that you can, intentionally, leave open 29 | // (and which close themselves) 30 | var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr"); 31 | 32 | // Attributes that have their values filled in disabled="disabled" 33 | var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"); 34 | 35 | // Special Elements (can contain anything) 36 | var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block"); 37 | 38 | function HTMLParser(html, handler) { 39 | var index, chars, match, stack = [], last = html; 40 | stack.last = function () { 41 | return this[this.length - 1]; 42 | }; 43 | 44 | while (html) { 45 | chars = true; 46 | 47 | // Make sure we're not in a script or style element 48 | if (!stack.last() || !special[stack.last()]) { 49 | 50 | // Comment 51 | if (html.indexOf(""); 53 | 54 | if (index >= 0) { 55 | if (handler.comment) 56 | handler.comment(html.substring(4, index)); 57 | html = html.substring(index + 3); 58 | chars = false; 59 | } 60 | 61 | // end tag 62 | } else if (html.indexOf("]*>"), function (all, text) { 100 | text = text.replace(/|/g, "$1$2"); 101 | if (handler.chars) 102 | handler.chars(text); 103 | 104 | return ""; 105 | }); 106 | 107 | 108 | parseEndTag("", stack.last()); 109 | } 110 | 111 | if (html == last) 112 | throw "Parse Error: " + html; 113 | last = html; 114 | } 115 | 116 | // Clean up any remaining tags 117 | parseEndTag(); 118 | 119 | function parseStartTag(tag, tagName, rest, unary) { 120 | tagName = tagName.toLowerCase(); 121 | 122 | if (block[tagName]) { 123 | while (stack.last() && inline[stack.last()]) { 124 | parseEndTag("", stack.last()); 125 | } 126 | } 127 | 128 | if (closeSelf[tagName] && stack.last() == tagName) { 129 | parseEndTag("", tagName); 130 | } 131 | 132 | unary = empty[tagName] || !!unary; 133 | 134 | if (!unary) 135 | stack.push(tagName); 136 | 137 | if (handler.start) { 138 | var attrs = []; 139 | 140 | rest.replace(attr, function (match, name) { 141 | var value = arguments[2] ? arguments[2] : 142 | arguments[3] ? arguments[3] : 143 | arguments[4] ? arguments[4] : 144 | fillAttrs[name] ? name : ""; 145 | 146 | attrs.push({ 147 | name: name, 148 | value: value, 149 | escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //" 150 | }); 151 | }); 152 | 153 | if (handler.start) { 154 | handler.start(tagName, attrs, unary); 155 | } 156 | 157 | } 158 | } 159 | 160 | function parseEndTag(tag, tagName) { 161 | // If no tag name is provided, clean shop 162 | if (!tagName) 163 | var pos = 0; 164 | 165 | // Find the closest opened tag of the same type 166 | else { 167 | tagName = tagName.toLowerCase(); 168 | for (var pos = stack.length - 1; pos >= 0; pos--) 169 | if (stack[pos] == tagName) 170 | break; 171 | } 172 | if (pos >= 0) { 173 | // Close all the open elements, up the stack 174 | for (var i = stack.length - 1; i >= pos; i--) 175 | if (handler.end) 176 | handler.end(stack[i]); 177 | 178 | // Remove the open elements from the stack 179 | stack.length = pos; 180 | } 181 | } 182 | }; 183 | 184 | 185 | function makeMap(str) { 186 | var obj = {}, items = str.split(","); 187 | for (var i = 0; i < items.length; i++) 188 | obj[items[i]] = true; 189 | return obj; 190 | } 191 | 192 | module.exports = HTMLParser; 193 | -------------------------------------------------------------------------------- /utils/wxParse/showdown.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * showdown: https://github.com/showdownjs/showdown 4 | * 5 | * author: Di (微信小程序开发工程师) 6 | * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) 7 | * 垂直微信小程序开发交流社区 8 | * 9 | * github地址: https://github.com/icindy/wxParse 10 | * 11 | * for: 微信小程序富文本解析 12 | * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 13 | */ 14 | 15 | function getDefaultOpts(simple) { 16 | 'use strict'; 17 | 18 | var defaultOptions = { 19 | omitExtraWLInCodeBlocks: { 20 | defaultValue: false, 21 | describe: 'Omit the default extra whiteline added to code blocks', 22 | type: 'boolean' 23 | }, 24 | noHeaderId: { 25 | defaultValue: false, 26 | describe: 'Turn on/off generated header id', 27 | type: 'boolean' 28 | }, 29 | prefixHeaderId: { 30 | defaultValue: false, 31 | describe: 'Specify a prefix to generated header ids', 32 | type: 'string' 33 | }, 34 | headerLevelStart: { 35 | defaultValue: false, 36 | describe: 'The header blocks level start', 37 | type: 'integer' 38 | }, 39 | parseImgDimensions: { 40 | defaultValue: false, 41 | describe: 'Turn on/off image dimension parsing', 42 | type: 'boolean' 43 | }, 44 | simplifiedAutoLink: { 45 | defaultValue: false, 46 | describe: 'Turn on/off GFM autolink style', 47 | type: 'boolean' 48 | }, 49 | literalMidWordUnderscores: { 50 | defaultValue: false, 51 | describe: 'Parse midword underscores as literal underscores', 52 | type: 'boolean' 53 | }, 54 | strikethrough: { 55 | defaultValue: false, 56 | describe: 'Turn on/off strikethrough support', 57 | type: 'boolean' 58 | }, 59 | tables: { 60 | defaultValue: false, 61 | describe: 'Turn on/off tables support', 62 | type: 'boolean' 63 | }, 64 | tablesHeaderId: { 65 | defaultValue: false, 66 | describe: 'Add an id to table headers', 67 | type: 'boolean' 68 | }, 69 | ghCodeBlocks: { 70 | defaultValue: true, 71 | describe: 'Turn on/off GFM fenced code blocks support', 72 | type: 'boolean' 73 | }, 74 | tasklists: { 75 | defaultValue: false, 76 | describe: 'Turn on/off GFM tasklist support', 77 | type: 'boolean' 78 | }, 79 | smoothLivePreview: { 80 | defaultValue: false, 81 | describe: 'Prevents weird effects in live previews due to incomplete input', 82 | type: 'boolean' 83 | }, 84 | smartIndentationFix: { 85 | defaultValue: false, 86 | description: 'Tries to smartly fix identation in es6 strings', 87 | type: 'boolean' 88 | } 89 | }; 90 | if (simple === false) { 91 | return JSON.parse(JSON.stringify(defaultOptions)); 92 | } 93 | var ret = {}; 94 | for (var opt in defaultOptions) { 95 | if (defaultOptions.hasOwnProperty(opt)) { 96 | ret[opt] = defaultOptions[opt].defaultValue; 97 | } 98 | } 99 | return ret; 100 | } 101 | 102 | /** 103 | * Created by Tivie on 06-01-2015. 104 | */ 105 | 106 | // Private properties 107 | var showdown = {}, 108 | parsers = {}, 109 | extensions = {}, 110 | globalOptions = getDefaultOpts(true), 111 | flavor = { 112 | github: { 113 | omitExtraWLInCodeBlocks: true, 114 | prefixHeaderId: 'user-content-', 115 | simplifiedAutoLink: true, 116 | literalMidWordUnderscores: true, 117 | strikethrough: true, 118 | tables: true, 119 | tablesHeaderId: true, 120 | ghCodeBlocks: true, 121 | tasklists: true 122 | }, 123 | vanilla: getDefaultOpts(true) 124 | }; 125 | 126 | /** 127 | * helper namespace 128 | * @type {{}} 129 | */ 130 | showdown.helper = {}; 131 | 132 | /** 133 | * TODO LEGACY SUPPORT CODE 134 | * @type {{}} 135 | */ 136 | showdown.extensions = {}; 137 | 138 | /** 139 | * Set a global option 140 | * @static 141 | * @param {string} key 142 | * @param {*} value 143 | * @returns {showdown} 144 | */ 145 | showdown.setOption = function (key, value) { 146 | 'use strict'; 147 | globalOptions[key] = value; 148 | return this; 149 | }; 150 | 151 | /** 152 | * Get a global option 153 | * @static 154 | * @param {string} key 155 | * @returns {*} 156 | */ 157 | showdown.getOption = function (key) { 158 | 'use strict'; 159 | return globalOptions[key]; 160 | }; 161 | 162 | /** 163 | * Get the global options 164 | * @static 165 | * @returns {{}} 166 | */ 167 | showdown.getOptions = function () { 168 | 'use strict'; 169 | return globalOptions; 170 | }; 171 | 172 | /** 173 | * Reset global options to the default values 174 | * @static 175 | */ 176 | showdown.resetOptions = function () { 177 | 'use strict'; 178 | globalOptions = getDefaultOpts(true); 179 | }; 180 | 181 | /** 182 | * Set the flavor showdown should use as default 183 | * @param {string} name 184 | */ 185 | showdown.setFlavor = function (name) { 186 | 'use strict'; 187 | if (flavor.hasOwnProperty(name)) { 188 | var preset = flavor[name]; 189 | for (var option in preset) { 190 | if (preset.hasOwnProperty(option)) { 191 | globalOptions[option] = preset[option]; 192 | } 193 | } 194 | } 195 | }; 196 | 197 | /** 198 | * Get the default options 199 | * @static 200 | * @param {boolean} [simple=true] 201 | * @returns {{}} 202 | */ 203 | showdown.getDefaultOptions = function (simple) { 204 | 'use strict'; 205 | return getDefaultOpts(simple); 206 | }; 207 | 208 | /** 209 | * Get or set a subParser 210 | * 211 | * subParser(name) - Get a registered subParser 212 | * subParser(name, func) - Register a subParser 213 | * @static 214 | * @param {string} name 215 | * @param {function} [func] 216 | * @returns {*} 217 | */ 218 | showdown.subParser = function (name, func) { 219 | 'use strict'; 220 | if (showdown.helper.isString(name)) { 221 | if (typeof func !== 'undefined') { 222 | parsers[name] = func; 223 | } else { 224 | if (parsers.hasOwnProperty(name)) { 225 | return parsers[name]; 226 | } else { 227 | throw Error('SubParser named ' + name + ' not registered!'); 228 | } 229 | } 230 | } 231 | }; 232 | 233 | /** 234 | * Gets or registers an extension 235 | * @static 236 | * @param {string} name 237 | * @param {object|function=} ext 238 | * @returns {*} 239 | */ 240 | showdown.extension = function (name, ext) { 241 | 'use strict'; 242 | 243 | if (!showdown.helper.isString(name)) { 244 | throw Error('Extension \'name\' must be a string'); 245 | } 246 | 247 | name = showdown.helper.stdExtName(name); 248 | 249 | // Getter 250 | if (showdown.helper.isUndefined(ext)) { 251 | if (!extensions.hasOwnProperty(name)) { 252 | throw Error('Extension named ' + name + ' is not registered!'); 253 | } 254 | return extensions[name]; 255 | 256 | // Setter 257 | } else { 258 | // Expand extension if it's wrapped in a function 259 | if (typeof ext === 'function') { 260 | ext = ext(); 261 | } 262 | 263 | // Ensure extension is an array 264 | if (!showdown.helper.isArray(ext)) { 265 | ext = [ext]; 266 | } 267 | 268 | var validExtension = validate(ext, name); 269 | 270 | if (validExtension.valid) { 271 | extensions[name] = ext; 272 | } else { 273 | throw Error(validExtension.error); 274 | } 275 | } 276 | }; 277 | 278 | /** 279 | * Gets all extensions registered 280 | * @returns {{}} 281 | */ 282 | showdown.getAllExtensions = function () { 283 | 'use strict'; 284 | return extensions; 285 | }; 286 | 287 | /** 288 | * Remove an extension 289 | * @param {string} name 290 | */ 291 | showdown.removeExtension = function (name) { 292 | 'use strict'; 293 | delete extensions[name]; 294 | }; 295 | 296 | /** 297 | * Removes all extensions 298 | */ 299 | showdown.resetExtensions = function () { 300 | 'use strict'; 301 | extensions = {}; 302 | }; 303 | 304 | /** 305 | * Validate extension 306 | * @param {array} extension 307 | * @param {string} name 308 | * @returns {{valid: boolean, error: string}} 309 | */ 310 | function validate(extension, name) { 311 | 'use strict'; 312 | 313 | var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension', 314 | ret = { 315 | valid: true, 316 | error: '' 317 | }; 318 | 319 | if (!showdown.helper.isArray(extension)) { 320 | extension = [extension]; 321 | } 322 | 323 | for (var i = 0; i < extension.length; ++i) { 324 | var baseMsg = errMsg + ' sub-extension ' + i + ': ', 325 | ext = extension[i]; 326 | if (typeof ext !== 'object') { 327 | ret.valid = false; 328 | ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given'; 329 | return ret; 330 | } 331 | 332 | if (!showdown.helper.isString(ext.type)) { 333 | ret.valid = false; 334 | ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given'; 335 | return ret; 336 | } 337 | 338 | var type = ext.type = ext.type.toLowerCase(); 339 | 340 | // normalize extension type 341 | if (type === 'language') { 342 | type = ext.type = 'lang'; 343 | } 344 | 345 | if (type === 'html') { 346 | type = ext.type = 'output'; 347 | } 348 | 349 | if (type !== 'lang' && type !== 'output' && type !== 'listener') { 350 | ret.valid = false; 351 | ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang/language", "output/html" or "listener"'; 352 | return ret; 353 | } 354 | 355 | if (type === 'listener') { 356 | if (showdown.helper.isUndefined(ext.listeners)) { 357 | ret.valid = false; 358 | ret.error = baseMsg + '. Extensions of type "listener" must have a property called "listeners"'; 359 | return ret; 360 | } 361 | } else { 362 | if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) { 363 | ret.valid = false; 364 | ret.error = baseMsg + type + ' extensions must define either a "regex" property or a "filter" method'; 365 | return ret; 366 | } 367 | } 368 | 369 | if (ext.listeners) { 370 | if (typeof ext.listeners !== 'object') { 371 | ret.valid = false; 372 | ret.error = baseMsg + '"listeners" property must be an object but ' + typeof ext.listeners + ' given'; 373 | return ret; 374 | } 375 | for (var ln in ext.listeners) { 376 | if (ext.listeners.hasOwnProperty(ln)) { 377 | if (typeof ext.listeners[ln] !== 'function') { 378 | ret.valid = false; 379 | ret.error = baseMsg + '"listeners" property must be an hash of [event name]: [callback]. listeners.' + ln + 380 | ' must be a function but ' + typeof ext.listeners[ln] + ' given'; 381 | return ret; 382 | } 383 | } 384 | } 385 | } 386 | 387 | if (ext.filter) { 388 | if (typeof ext.filter !== 'function') { 389 | ret.valid = false; 390 | ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given'; 391 | return ret; 392 | } 393 | } else if (ext.regex) { 394 | if (showdown.helper.isString(ext.regex)) { 395 | ext.regex = new RegExp(ext.regex, 'g'); 396 | } 397 | if (!ext.regex instanceof RegExp) { 398 | ret.valid = false; 399 | ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' + typeof ext.regex + ' given'; 400 | return ret; 401 | } 402 | if (showdown.helper.isUndefined(ext.replace)) { 403 | ret.valid = false; 404 | ret.error = baseMsg + '"regex" extensions must implement a replace string or function'; 405 | return ret; 406 | } 407 | } 408 | } 409 | return ret; 410 | } 411 | 412 | /** 413 | * Validate extension 414 | * @param {object} ext 415 | * @returns {boolean} 416 | */ 417 | showdown.validateExtension = function (ext) { 418 | 'use strict'; 419 | 420 | var validateExtension = validate(ext, null); 421 | if (!validateExtension.valid) { 422 | console.warn(validateExtension.error); 423 | return false; 424 | } 425 | return true; 426 | }; 427 | 428 | /** 429 | * showdownjs helper functions 430 | */ 431 | 432 | if (!showdown.hasOwnProperty('helper')) { 433 | showdown.helper = {}; 434 | } 435 | 436 | /** 437 | * Check if var is string 438 | * @static 439 | * @param {string} a 440 | * @returns {boolean} 441 | */ 442 | showdown.helper.isString = function isString(a) { 443 | 'use strict'; 444 | return (typeof a === 'string' || a instanceof String); 445 | }; 446 | 447 | /** 448 | * Check if var is a function 449 | * @static 450 | * @param {string} a 451 | * @returns {boolean} 452 | */ 453 | showdown.helper.isFunction = function isFunction(a) { 454 | 'use strict'; 455 | var getType = {}; 456 | return a && getType.toString.call(a) === '[object Function]'; 457 | }; 458 | 459 | /** 460 | * ForEach helper function 461 | * @static 462 | * @param {*} obj 463 | * @param {function} callback 464 | */ 465 | showdown.helper.forEach = function forEach(obj, callback) { 466 | 'use strict'; 467 | if (typeof obj.forEach === 'function') { 468 | obj.forEach(callback); 469 | } else { 470 | for (var i = 0; i < obj.length; i++) { 471 | callback(obj[i], i, obj); 472 | } 473 | } 474 | }; 475 | 476 | /** 477 | * isArray helper function 478 | * @static 479 | * @param {*} a 480 | * @returns {boolean} 481 | */ 482 | showdown.helper.isArray = function isArray(a) { 483 | 'use strict'; 484 | return a.constructor === Array; 485 | }; 486 | 487 | /** 488 | * Check if value is undefined 489 | * @static 490 | * @param {*} value The value to check. 491 | * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. 492 | */ 493 | showdown.helper.isUndefined = function isUndefined(value) { 494 | 'use strict'; 495 | return typeof value === 'undefined'; 496 | }; 497 | 498 | /** 499 | * Standardidize extension name 500 | * @static 501 | * @param {string} s extension name 502 | * @returns {string} 503 | */ 504 | showdown.helper.stdExtName = function (s) { 505 | 'use strict'; 506 | return s.replace(/[_-]||\s/g, '').toLowerCase(); 507 | }; 508 | 509 | function escapeCharactersCallback(wholeMatch, m1) { 510 | 'use strict'; 511 | var charCodeToEscape = m1.charCodeAt(0); 512 | return '~E' + charCodeToEscape + 'E'; 513 | } 514 | 515 | /** 516 | * Callback used to escape characters when passing through String.replace 517 | * @static 518 | * @param {string} wholeMatch 519 | * @param {string} m1 520 | * @returns {string} 521 | */ 522 | showdown.helper.escapeCharactersCallback = escapeCharactersCallback; 523 | 524 | /** 525 | * Escape characters in a string 526 | * @static 527 | * @param {string} text 528 | * @param {string} charsToEscape 529 | * @param {boolean} afterBackslash 530 | * @returns {XML|string|void|*} 531 | */ 532 | showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) { 533 | 'use strict'; 534 | // First we have to escape the escape characters so that 535 | // we can build a character class out of them 536 | var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])'; 537 | 538 | if (afterBackslash) { 539 | regexString = '\\\\' + regexString; 540 | } 541 | 542 | var regex = new RegExp(regexString, 'g'); 543 | text = text.replace(regex, escapeCharactersCallback); 544 | 545 | return text; 546 | }; 547 | 548 | var rgxFindMatchPos = function (str, left, right, flags) { 549 | 'use strict'; 550 | var f = flags || '', 551 | g = f.indexOf('g') > -1, 552 | x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, '')), 553 | l = new RegExp(left, f.replace(/g/g, '')), 554 | pos = [], 555 | t, s, m, start, end; 556 | 557 | do { 558 | t = 0; 559 | while ((m = x.exec(str))) { 560 | if (l.test(m[0])) { 561 | if (!(t++)) { 562 | s = x.lastIndex; 563 | start = s - m[0].length; 564 | } 565 | } else if (t) { 566 | if (!--t) { 567 | end = m.index + m[0].length; 568 | var obj = { 569 | left: {start: start, end: s}, 570 | match: {start: s, end: m.index}, 571 | right: {start: m.index, end: end}, 572 | wholeMatch: {start: start, end: end} 573 | }; 574 | pos.push(obj); 575 | if (!g) { 576 | return pos; 577 | } 578 | } 579 | } 580 | } 581 | } while (t && (x.lastIndex = s)); 582 | 583 | return pos; 584 | }; 585 | 586 | /** 587 | * matchRecursiveRegExp 588 | * 589 | * (c) 2007 Steven Levithan 590 | * MIT License 591 | * 592 | * Accepts a string to search, a left and right format delimiter 593 | * as regex patterns, and optional regex flags. Returns an array 594 | * of matches, allowing nested instances of left/right delimiters. 595 | * Use the "g" flag to return all matches, otherwise only the 596 | * first is returned. Be careful to ensure that the left and 597 | * right format delimiters produce mutually exclusive matches. 598 | * Backreferences are not supported within the right delimiter 599 | * due to how it is internally combined with the left delimiter. 600 | * When matching strings whose format delimiters are unbalanced 601 | * to the left or right, the output is intentionally as a 602 | * conventional regex library with recursion support would 603 | * produce, e.g. "<" and ">" both produce ["x"] when using 604 | * "<" and ">" as the delimiters (both strings contain a single, 605 | * balanced instance of ""). 606 | * 607 | * examples: 608 | * matchRecursiveRegExp("test", "\\(", "\\)") 609 | * returns: [] 610 | * matchRecursiveRegExp(">>t<>", "<", ">", "g") 611 | * returns: ["t<>", ""] 612 | * matchRecursiveRegExp("
test
", "]*>", "", "gi") 613 | * returns: ["test"] 614 | */ 615 | showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) { 616 | 'use strict'; 617 | 618 | var matchPos = rgxFindMatchPos (str, left, right, flags), 619 | results = []; 620 | 621 | for (var i = 0; i < matchPos.length; ++i) { 622 | results.push([ 623 | str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end), 624 | str.slice(matchPos[i].match.start, matchPos[i].match.end), 625 | str.slice(matchPos[i].left.start, matchPos[i].left.end), 626 | str.slice(matchPos[i].right.start, matchPos[i].right.end) 627 | ]); 628 | } 629 | return results; 630 | }; 631 | 632 | /** 633 | * 634 | * @param {string} str 635 | * @param {string|function} replacement 636 | * @param {string} left 637 | * @param {string} right 638 | * @param {string} flags 639 | * @returns {string} 640 | */ 641 | showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) { 642 | 'use strict'; 643 | 644 | if (!showdown.helper.isFunction(replacement)) { 645 | var repStr = replacement; 646 | replacement = function () { 647 | return repStr; 648 | }; 649 | } 650 | 651 | var matchPos = rgxFindMatchPos(str, left, right, flags), 652 | finalStr = str, 653 | lng = matchPos.length; 654 | 655 | if (lng > 0) { 656 | var bits = []; 657 | if (matchPos[0].wholeMatch.start !== 0) { 658 | bits.push(str.slice(0, matchPos[0].wholeMatch.start)); 659 | } 660 | for (var i = 0; i < lng; ++i) { 661 | bits.push( 662 | replacement( 663 | str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end), 664 | str.slice(matchPos[i].match.start, matchPos[i].match.end), 665 | str.slice(matchPos[i].left.start, matchPos[i].left.end), 666 | str.slice(matchPos[i].right.start, matchPos[i].right.end) 667 | ) 668 | ); 669 | if (i < lng - 1) { 670 | bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start)); 671 | } 672 | } 673 | if (matchPos[lng - 1].wholeMatch.end < str.length) { 674 | bits.push(str.slice(matchPos[lng - 1].wholeMatch.end)); 675 | } 676 | finalStr = bits.join(''); 677 | } 678 | return finalStr; 679 | }; 680 | 681 | /** 682 | * POLYFILLS 683 | */ 684 | if (showdown.helper.isUndefined(console)) { 685 | console = { 686 | warn: function (msg) { 687 | 'use strict'; 688 | alert(msg); 689 | }, 690 | log: function (msg) { 691 | 'use strict'; 692 | alert(msg); 693 | }, 694 | error: function (msg) { 695 | 'use strict'; 696 | throw msg; 697 | } 698 | }; 699 | } 700 | 701 | /** 702 | * Created by Estevao on 31-05-2015. 703 | */ 704 | 705 | /** 706 | * Showdown Converter class 707 | * @class 708 | * @param {object} [converterOptions] 709 | * @returns {Converter} 710 | */ 711 | showdown.Converter = function (converterOptions) { 712 | 'use strict'; 713 | 714 | var 715 | /** 716 | * Options used by this converter 717 | * @private 718 | * @type {{}} 719 | */ 720 | options = {}, 721 | 722 | /** 723 | * Language extensions used by this converter 724 | * @private 725 | * @type {Array} 726 | */ 727 | langExtensions = [], 728 | 729 | /** 730 | * Output modifiers extensions used by this converter 731 | * @private 732 | * @type {Array} 733 | */ 734 | outputModifiers = [], 735 | 736 | /** 737 | * Event listeners 738 | * @private 739 | * @type {{}} 740 | */ 741 | listeners = {}; 742 | 743 | _constructor(); 744 | 745 | /** 746 | * Converter constructor 747 | * @private 748 | */ 749 | function _constructor() { 750 | converterOptions = converterOptions || {}; 751 | 752 | for (var gOpt in globalOptions) { 753 | if (globalOptions.hasOwnProperty(gOpt)) { 754 | options[gOpt] = globalOptions[gOpt]; 755 | } 756 | } 757 | 758 | // Merge options 759 | if (typeof converterOptions === 'object') { 760 | for (var opt in converterOptions) { 761 | if (converterOptions.hasOwnProperty(opt)) { 762 | options[opt] = converterOptions[opt]; 763 | } 764 | } 765 | } else { 766 | throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions + 767 | ' was passed instead.'); 768 | } 769 | 770 | if (options.extensions) { 771 | showdown.helper.forEach(options.extensions, _parseExtension); 772 | } 773 | } 774 | 775 | /** 776 | * Parse extension 777 | * @param {*} ext 778 | * @param {string} [name=''] 779 | * @private 780 | */ 781 | function _parseExtension(ext, name) { 782 | 783 | name = name || null; 784 | // If it's a string, the extension was previously loaded 785 | if (showdown.helper.isString(ext)) { 786 | ext = showdown.helper.stdExtName(ext); 787 | name = ext; 788 | 789 | // LEGACY_SUPPORT CODE 790 | if (showdown.extensions[ext]) { 791 | console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' + 792 | 'Please inform the developer that the extension should be updated!'); 793 | legacyExtensionLoading(showdown.extensions[ext], ext); 794 | return; 795 | // END LEGACY SUPPORT CODE 796 | 797 | } else if (!showdown.helper.isUndefined(extensions[ext])) { 798 | ext = extensions[ext]; 799 | 800 | } else { 801 | throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.'); 802 | } 803 | } 804 | 805 | if (typeof ext === 'function') { 806 | ext = ext(); 807 | } 808 | 809 | if (!showdown.helper.isArray(ext)) { 810 | ext = [ext]; 811 | } 812 | 813 | var validExt = validate(ext, name); 814 | if (!validExt.valid) { 815 | throw Error(validExt.error); 816 | } 817 | 818 | for (var i = 0; i < ext.length; ++i) { 819 | switch (ext[i].type) { 820 | 821 | case 'lang': 822 | langExtensions.push(ext[i]); 823 | break; 824 | 825 | case 'output': 826 | outputModifiers.push(ext[i]); 827 | break; 828 | } 829 | if (ext[i].hasOwnProperty(listeners)) { 830 | for (var ln in ext[i].listeners) { 831 | if (ext[i].listeners.hasOwnProperty(ln)) { 832 | listen(ln, ext[i].listeners[ln]); 833 | } 834 | } 835 | } 836 | } 837 | 838 | } 839 | 840 | /** 841 | * LEGACY_SUPPORT 842 | * @param {*} ext 843 | * @param {string} name 844 | */ 845 | function legacyExtensionLoading(ext, name) { 846 | if (typeof ext === 'function') { 847 | ext = ext(new showdown.Converter()); 848 | } 849 | if (!showdown.helper.isArray(ext)) { 850 | ext = [ext]; 851 | } 852 | var valid = validate(ext, name); 853 | 854 | if (!valid.valid) { 855 | throw Error(valid.error); 856 | } 857 | 858 | for (var i = 0; i < ext.length; ++i) { 859 | switch (ext[i].type) { 860 | case 'lang': 861 | langExtensions.push(ext[i]); 862 | break; 863 | case 'output': 864 | outputModifiers.push(ext[i]); 865 | break; 866 | default:// should never reach here 867 | throw Error('Extension loader error: Type unrecognized!!!'); 868 | } 869 | } 870 | } 871 | 872 | /** 873 | * Listen to an event 874 | * @param {string} name 875 | * @param {function} callback 876 | */ 877 | function listen(name, callback) { 878 | if (!showdown.helper.isString(name)) { 879 | throw Error('Invalid argument in converter.listen() method: name must be a string, but ' + typeof name + ' given'); 880 | } 881 | 882 | if (typeof callback !== 'function') { 883 | throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given'); 884 | } 885 | 886 | if (!listeners.hasOwnProperty(name)) { 887 | listeners[name] = []; 888 | } 889 | listeners[name].push(callback); 890 | } 891 | 892 | function rTrimInputText(text) { 893 | var rsp = text.match(/^\s*/)[0].length, 894 | rgx = new RegExp('^\\s{0,' + rsp + '}', 'gm'); 895 | return text.replace(rgx, ''); 896 | } 897 | 898 | /** 899 | * Dispatch an event 900 | * @private 901 | * @param {string} evtName Event name 902 | * @param {string} text Text 903 | * @param {{}} options Converter Options 904 | * @param {{}} globals 905 | * @returns {string} 906 | */ 907 | this._dispatch = function dispatch (evtName, text, options, globals) { 908 | if (listeners.hasOwnProperty(evtName)) { 909 | for (var ei = 0; ei < listeners[evtName].length; ++ei) { 910 | var nText = listeners[evtName][ei](evtName, text, this, options, globals); 911 | if (nText && typeof nText !== 'undefined') { 912 | text = nText; 913 | } 914 | } 915 | } 916 | return text; 917 | }; 918 | 919 | /** 920 | * Listen to an event 921 | * @param {string} name 922 | * @param {function} callback 923 | * @returns {showdown.Converter} 924 | */ 925 | this.listen = function (name, callback) { 926 | listen(name, callback); 927 | return this; 928 | }; 929 | 930 | /** 931 | * Converts a markdown string into HTML 932 | * @param {string} text 933 | * @returns {*} 934 | */ 935 | this.makeHtml = function (text) { 936 | //check if text is not falsy 937 | if (!text) { 938 | return text; 939 | } 940 | 941 | var globals = { 942 | gHtmlBlocks: [], 943 | gHtmlMdBlocks: [], 944 | gHtmlSpans: [], 945 | gUrls: {}, 946 | gTitles: {}, 947 | gDimensions: {}, 948 | gListLevel: 0, 949 | hashLinkCounts: {}, 950 | langExtensions: langExtensions, 951 | outputModifiers: outputModifiers, 952 | converter: this, 953 | ghCodeBlocks: [] 954 | }; 955 | 956 | // attacklab: Replace ~ with ~T 957 | // This lets us use tilde as an escape char to avoid md5 hashes 958 | // The choice of character is arbitrary; anything that isn't 959 | // magic in Markdown will work. 960 | text = text.replace(/~/g, '~T'); 961 | 962 | // attacklab: Replace $ with ~D 963 | // RegExp interprets $ as a special character 964 | // when it's in a replacement string 965 | text = text.replace(/\$/g, '~D'); 966 | 967 | // Standardize line endings 968 | text = text.replace(/\r\n/g, '\n'); // DOS to Unix 969 | text = text.replace(/\r/g, '\n'); // Mac to Unix 970 | 971 | if (options.smartIndentationFix) { 972 | text = rTrimInputText(text); 973 | } 974 | 975 | // Make sure text begins and ends with a couple of newlines: 976 | //text = '\n\n' + text + '\n\n'; 977 | text = text; 978 | // detab 979 | text = showdown.subParser('detab')(text, options, globals); 980 | 981 | // stripBlankLines 982 | text = showdown.subParser('stripBlankLines')(text, options, globals); 983 | 984 | //run languageExtensions 985 | showdown.helper.forEach(langExtensions, function (ext) { 986 | text = showdown.subParser('runExtension')(ext, text, options, globals); 987 | }); 988 | 989 | // run the sub parsers 990 | text = showdown.subParser('hashPreCodeTags')(text, options, globals); 991 | text = showdown.subParser('githubCodeBlocks')(text, options, globals); 992 | text = showdown.subParser('hashHTMLBlocks')(text, options, globals); 993 | text = showdown.subParser('hashHTMLSpans')(text, options, globals); 994 | text = showdown.subParser('stripLinkDefinitions')(text, options, globals); 995 | text = showdown.subParser('blockGamut')(text, options, globals); 996 | text = showdown.subParser('unhashHTMLSpans')(text, options, globals); 997 | text = showdown.subParser('unescapeSpecialChars')(text, options, globals); 998 | 999 | // attacklab: Restore dollar signs 1000 | text = text.replace(/~D/g, '$$'); 1001 | 1002 | // attacklab: Restore tildes 1003 | text = text.replace(/~T/g, '~'); 1004 | 1005 | // Run output modifiers 1006 | showdown.helper.forEach(outputModifiers, function (ext) { 1007 | text = showdown.subParser('runExtension')(ext, text, options, globals); 1008 | }); 1009 | return text; 1010 | }; 1011 | 1012 | /** 1013 | * Set an option of this Converter instance 1014 | * @param {string} key 1015 | * @param {*} value 1016 | */ 1017 | this.setOption = function (key, value) { 1018 | options[key] = value; 1019 | }; 1020 | 1021 | /** 1022 | * Get the option of this Converter instance 1023 | * @param {string} key 1024 | * @returns {*} 1025 | */ 1026 | this.getOption = function (key) { 1027 | return options[key]; 1028 | }; 1029 | 1030 | /** 1031 | * Get the options of this Converter instance 1032 | * @returns {{}} 1033 | */ 1034 | this.getOptions = function () { 1035 | return options; 1036 | }; 1037 | 1038 | /** 1039 | * Add extension to THIS converter 1040 | * @param {{}} extension 1041 | * @param {string} [name=null] 1042 | */ 1043 | this.addExtension = function (extension, name) { 1044 | name = name || null; 1045 | _parseExtension(extension, name); 1046 | }; 1047 | 1048 | /** 1049 | * Use a global registered extension with THIS converter 1050 | * @param {string} extensionName Name of the previously registered extension 1051 | */ 1052 | this.useExtension = function (extensionName) { 1053 | _parseExtension(extensionName); 1054 | }; 1055 | 1056 | /** 1057 | * Set the flavor THIS converter should use 1058 | * @param {string} name 1059 | */ 1060 | this.setFlavor = function (name) { 1061 | if (flavor.hasOwnProperty(name)) { 1062 | var preset = flavor[name]; 1063 | for (var option in preset) { 1064 | if (preset.hasOwnProperty(option)) { 1065 | options[option] = preset[option]; 1066 | } 1067 | } 1068 | } 1069 | }; 1070 | 1071 | /** 1072 | * Remove an extension from THIS converter. 1073 | * Note: This is a costly operation. It's better to initialize a new converter 1074 | * and specify the extensions you wish to use 1075 | * @param {Array} extension 1076 | */ 1077 | this.removeExtension = function (extension) { 1078 | if (!showdown.helper.isArray(extension)) { 1079 | extension = [extension]; 1080 | } 1081 | for (var a = 0; a < extension.length; ++a) { 1082 | var ext = extension[a]; 1083 | for (var i = 0; i < langExtensions.length; ++i) { 1084 | if (langExtensions[i] === ext) { 1085 | langExtensions[i].splice(i, 1); 1086 | } 1087 | } 1088 | for (var ii = 0; ii < outputModifiers.length; ++i) { 1089 | if (outputModifiers[ii] === ext) { 1090 | outputModifiers[ii].splice(i, 1); 1091 | } 1092 | } 1093 | } 1094 | }; 1095 | 1096 | /** 1097 | * Get all extension of THIS converter 1098 | * @returns {{language: Array, output: Array}} 1099 | */ 1100 | this.getAllExtensions = function () { 1101 | return { 1102 | language: langExtensions, 1103 | output: outputModifiers 1104 | }; 1105 | }; 1106 | }; 1107 | 1108 | /** 1109 | * Turn Markdown link shortcuts into XHTML tags. 1110 | */ 1111 | showdown.subParser('anchors', function (text, options, globals) { 1112 | 'use strict'; 1113 | 1114 | text = globals.converter._dispatch('anchors.before', text, options, globals); 1115 | 1116 | var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) { 1117 | if (showdown.helper.isUndefined(m7)) { 1118 | m7 = ''; 1119 | } 1120 | wholeMatch = m1; 1121 | var linkText = m2, 1122 | linkId = m3.toLowerCase(), 1123 | url = m4, 1124 | title = m7; 1125 | 1126 | if (!url) { 1127 | if (!linkId) { 1128 | // lower-case and turn embedded newlines into spaces 1129 | linkId = linkText.toLowerCase().replace(/ ?\n/g, ' '); 1130 | } 1131 | url = '#' + linkId; 1132 | 1133 | if (!showdown.helper.isUndefined(globals.gUrls[linkId])) { 1134 | url = globals.gUrls[linkId]; 1135 | if (!showdown.helper.isUndefined(globals.gTitles[linkId])) { 1136 | title = globals.gTitles[linkId]; 1137 | } 1138 | } else { 1139 | if (wholeMatch.search(/\(\s*\)$/m) > -1) { 1140 | // Special case for explicit empty url 1141 | url = ''; 1142 | } else { 1143 | return wholeMatch; 1144 | } 1145 | } 1146 | } 1147 | 1148 | url = showdown.helper.escapeCharacters(url, '*_', false); 1149 | var result = ''; 1158 | 1159 | return result; 1160 | }; 1161 | 1162 | // First, handle reference-style links: [link text] [id] 1163 | /* 1164 | text = text.replace(/ 1165 | ( // wrap whole match in $1 1166 | \[ 1167 | ( 1168 | (?: 1169 | \[[^\]]*\] // allow brackets nested one level 1170 | | 1171 | [^\[] // or anything else 1172 | )* 1173 | ) 1174 | \] 1175 | 1176 | [ ]? // one optional space 1177 | (?:\n[ ]*)? // one optional newline followed by spaces 1178 | 1179 | \[ 1180 | (.*?) // id = $3 1181 | \] 1182 | )()()()() // pad remaining backreferences 1183 | /g,_DoAnchors_callback); 1184 | */ 1185 | text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)][ ]?(?:\n[ ]*)?\[(.*?)])()()()()/g, writeAnchorTag); 1186 | 1187 | // 1188 | // Next, inline-style links: [link text](url "optional title") 1189 | // 1190 | 1191 | /* 1192 | text = text.replace(/ 1193 | ( // wrap whole match in $1 1194 | \[ 1195 | ( 1196 | (?: 1197 | \[[^\]]*\] // allow brackets nested one level 1198 | | 1199 | [^\[\]] // or anything else 1200 | ) 1201 | ) 1202 | \] 1203 | \( // literal paren 1204 | [ \t]* 1205 | () // no id, so leave $3 empty 1206 | ? // href = $4 1207 | [ \t]* 1208 | ( // $5 1209 | (['"]) // quote char = $6 1210 | (.*?) // Title = $7 1211 | \6 // matching quote 1212 | [ \t]* // ignore any spaces/tabs between closing quote and ) 1213 | )? // title is optional 1214 | \) 1215 | ) 1216 | /g,writeAnchorTag); 1217 | */ 1218 | text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, 1219 | writeAnchorTag); 1220 | 1221 | // 1222 | // Last, handle reference-style shortcuts: [link text] 1223 | // These must come last in case you've also got [link test][1] 1224 | // or [link test](/foo) 1225 | // 1226 | 1227 | /* 1228 | text = text.replace(/ 1229 | ( // wrap whole match in $1 1230 | \[ 1231 | ([^\[\]]+) // link text = $2; can't contain '[' or ']' 1232 | \] 1233 | )()()()()() // pad rest of backreferences 1234 | /g, writeAnchorTag); 1235 | */ 1236 | text = text.replace(/(\[([^\[\]]+)])()()()()()/g, writeAnchorTag); 1237 | 1238 | text = globals.converter._dispatch('anchors.after', text, options, globals); 1239 | return text; 1240 | }); 1241 | 1242 | showdown.subParser('autoLinks', function (text, options, globals) { 1243 | 'use strict'; 1244 | 1245 | text = globals.converter._dispatch('autoLinks.before', text, options, globals); 1246 | 1247 | var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi, 1248 | delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi, 1249 | simpleMailRegex = /(?:^|[ \n\t])([A-Za-z0-9!#$%&'*+-/=?^_`\{|}~\.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?:$|[ \n\t])/gi, 1250 | delimMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi; 1251 | 1252 | text = text.replace(delimUrlRegex, replaceLink); 1253 | text = text.replace(delimMailRegex, replaceMail); 1254 | // simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi, 1255 | // Email addresses: 1256 | 1257 | if (options.simplifiedAutoLink) { 1258 | text = text.replace(simpleURLRegex, replaceLink); 1259 | text = text.replace(simpleMailRegex, replaceMail); 1260 | } 1261 | 1262 | function replaceLink(wm, link) { 1263 | var lnkTxt = link; 1264 | if (/^www\./i.test(link)) { 1265 | link = link.replace(/^www\./i, 'http://www.'); 1266 | } 1267 | return '' + lnkTxt + ''; 1268 | } 1269 | 1270 | function replaceMail(wholeMatch, m1) { 1271 | var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1); 1272 | return showdown.subParser('encodeEmailAddress')(unescapedStr); 1273 | } 1274 | 1275 | text = globals.converter._dispatch('autoLinks.after', text, options, globals); 1276 | 1277 | return text; 1278 | }); 1279 | 1280 | /** 1281 | * These are all the transformations that form block-level 1282 | * tags like paragraphs, headers, and list items. 1283 | */ 1284 | showdown.subParser('blockGamut', function (text, options, globals) { 1285 | 'use strict'; 1286 | 1287 | text = globals.converter._dispatch('blockGamut.before', text, options, globals); 1288 | 1289 | // we parse blockquotes first so that we can have headings and hrs 1290 | // inside blockquotes 1291 | text = showdown.subParser('blockQuotes')(text, options, globals); 1292 | text = showdown.subParser('headers')(text, options, globals); 1293 | 1294 | // Do Horizontal Rules: 1295 | var key = showdown.subParser('hashBlock')('
', options, globals); 1296 | text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key); 1297 | text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key); 1298 | text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key); 1299 | 1300 | text = showdown.subParser('lists')(text, options, globals); 1301 | text = showdown.subParser('codeBlocks')(text, options, globals); 1302 | text = showdown.subParser('tables')(text, options, globals); 1303 | 1304 | // We already ran _HashHTMLBlocks() before, in Markdown(), but that 1305 | // was to escape raw HTML in the original Markdown source. This time, 1306 | // we're escaping the markup we've just created, so that we don't wrap 1307 | //

tags around block-level tags. 1308 | text = showdown.subParser('hashHTMLBlocks')(text, options, globals); 1309 | text = showdown.subParser('paragraphs')(text, options, globals); 1310 | 1311 | text = globals.converter._dispatch('blockGamut.after', text, options, globals); 1312 | 1313 | return text; 1314 | }); 1315 | 1316 | showdown.subParser('blockQuotes', function (text, options, globals) { 1317 | 'use strict'; 1318 | 1319 | text = globals.converter._dispatch('blockQuotes.before', text, options, globals); 1320 | /* 1321 | text = text.replace(/ 1322 | ( // Wrap whole match in $1 1323 | ( 1324 | ^[ \t]*>[ \t]? // '>' at the start of a line 1325 | .+\n // rest of the first line 1326 | (.+\n)* // subsequent consecutive lines 1327 | \n* // blanks 1328 | )+ 1329 | ) 1330 | /gm, function(){...}); 1331 | */ 1332 | 1333 | text = text.replace(/((^[ \t]{0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) { 1334 | var bq = m1; 1335 | 1336 | // attacklab: hack around Konqueror 3.5.4 bug: 1337 | // "----------bug".replace(/^-/g,"") == "bug" 1338 | bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0'); // trim one level of quoting 1339 | 1340 | // attacklab: clean up hack 1341 | bq = bq.replace(/~0/g, ''); 1342 | 1343 | bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines 1344 | bq = showdown.subParser('githubCodeBlocks')(bq, options, globals); 1345 | bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse 1346 | 1347 | bq = bq.replace(/(^|\n)/g, '$1 '); 1348 | // These leading spaces screw with

 content, so we need to fix that:
1349 |     bq = bq.replace(/(\s*
[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
1350 |       var pre = m1;
1351 |       // attacklab: hack around Konqueror 3.5.4 bug:
1352 |       pre = pre.replace(/^  /mg, '~0');
1353 |       pre = pre.replace(/~0/g, '');
1354 |       return pre;
1355 |     });
1356 | 
1357 |     return showdown.subParser('hashBlock')('
\n' + bq + '\n
', options, globals); 1358 | }); 1359 | 1360 | text = globals.converter._dispatch('blockQuotes.after', text, options, globals); 1361 | return text; 1362 | }); 1363 | 1364 | /** 1365 | * Process Markdown `
` blocks.
1366 |  */
1367 | showdown.subParser('codeBlocks', function (text, options, globals) {
1368 |   'use strict';
1369 | 
1370 |   text = globals.converter._dispatch('codeBlocks.before', text, options, globals);
1371 |   /*
1372 |    text = text.replace(text,
1373 |    /(?:\n\n|^)
1374 |    (								// $1 = the code block -- one or more lines, starting with a space/tab
1375 |    (?:
1376 |    (?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
1377 |    .*\n+
1378 |    )+
1379 |    )
1380 |    (\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
1381 |    /g,function(){...});
1382 |    */
1383 | 
1384 |   // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
1385 |   text += '~0';
1386 | 
1387 |   var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;
1388 |   text = text.replace(pattern, function (wholeMatch, m1, m2) {
1389 |     var codeblock = m1,
1390 |         nextChar = m2,
1391 |         end = '\n';
1392 | 
1393 |     codeblock = showdown.subParser('outdent')(codeblock);
1394 |     codeblock = showdown.subParser('encodeCode')(codeblock);
1395 |     codeblock = showdown.subParser('detab')(codeblock);
1396 |     codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
1397 |     codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
1398 | 
1399 |     if (options.omitExtraWLInCodeBlocks) {
1400 |       end = '';
1401 |     }
1402 | 
1403 |     codeblock = '
' + codeblock + end + '
'; 1404 | 1405 | return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar; 1406 | }); 1407 | 1408 | // attacklab: strip sentinel 1409 | text = text.replace(/~0/, ''); 1410 | 1411 | text = globals.converter._dispatch('codeBlocks.after', text, options, globals); 1412 | return text; 1413 | }); 1414 | 1415 | /** 1416 | * 1417 | * * Backtick quotes are used for spans. 1418 | * 1419 | * * You can use multiple backticks as the delimiters if you want to 1420 | * include literal backticks in the code span. So, this input: 1421 | * 1422 | * Just type ``foo `bar` baz`` at the prompt. 1423 | * 1424 | * Will translate to: 1425 | * 1426 | *

Just type foo `bar` baz at the prompt.

1427 | * 1428 | * There's no arbitrary limit to the number of backticks you 1429 | * can use as delimters. If you need three consecutive backticks 1430 | * in your code, use four for delimiters, etc. 1431 | * 1432 | * * You can use spaces to get literal backticks at the edges: 1433 | * 1434 | * ... type `` `bar` `` ... 1435 | * 1436 | * Turns to: 1437 | * 1438 | * ... type `bar` ... 1439 | */ 1440 | showdown.subParser('codeSpans', function (text, options, globals) { 1441 | 'use strict'; 1442 | 1443 | text = globals.converter._dispatch('codeSpans.before', text, options, globals); 1444 | 1445 | /* 1446 | text = text.replace(/ 1447 | (^|[^\\]) // Character before opening ` can't be a backslash 1448 | (`+) // $2 = Opening run of ` 1449 | ( // $3 = The code block 1450 | [^\r]*? 1451 | [^`] // attacklab: work around lack of lookbehind 1452 | ) 1453 | \2 // Matching closer 1454 | (?!`) 1455 | /gm, function(){...}); 1456 | */ 1457 | 1458 | if (typeof(text) === 'undefined') { 1459 | text = ''; 1460 | } 1461 | text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, 1462 | function (wholeMatch, m1, m2, m3) { 1463 | var c = m3; 1464 | c = c.replace(/^([ \t]*)/g, ''); // leading whitespace 1465 | c = c.replace(/[ \t]*$/g, ''); // trailing whitespace 1466 | c = showdown.subParser('encodeCode')(c); 1467 | return m1 + '' + c + ''; 1468 | } 1469 | ); 1470 | 1471 | text = globals.converter._dispatch('codeSpans.after', text, options, globals); 1472 | return text; 1473 | }); 1474 | 1475 | /** 1476 | * Convert all tabs to spaces 1477 | */ 1478 | showdown.subParser('detab', function (text) { 1479 | 'use strict'; 1480 | 1481 | // expand first n-1 tabs 1482 | text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width 1483 | 1484 | // replace the nth with two sentinels 1485 | text = text.replace(/\t/g, '~A~B'); 1486 | 1487 | // use the sentinel to anchor our regex so it doesn't explode 1488 | text = text.replace(/~B(.+?)~A/g, function (wholeMatch, m1) { 1489 | var leadingText = m1, 1490 | numSpaces = 4 - leadingText.length % 4; // g_tab_width 1491 | 1492 | // there *must* be a better way to do this: 1493 | for (var i = 0; i < numSpaces; i++) { 1494 | leadingText += ' '; 1495 | } 1496 | 1497 | return leadingText; 1498 | }); 1499 | 1500 | // clean up sentinels 1501 | text = text.replace(/~A/g, ' '); // g_tab_width 1502 | text = text.replace(/~B/g, ''); 1503 | 1504 | return text; 1505 | 1506 | }); 1507 | 1508 | /** 1509 | * Smart processing for ampersands and angle brackets that need to be encoded. 1510 | */ 1511 | showdown.subParser('encodeAmpsAndAngles', function (text) { 1512 | 'use strict'; 1513 | // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: 1514 | // http://bumppo.net/projects/amputator/ 1515 | text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&'); 1516 | 1517 | // Encode naked <'s 1518 | text = text.replace(/<(?![a-z\/?\$!])/gi, '<'); 1519 | 1520 | return text; 1521 | }); 1522 | 1523 | /** 1524 | * Returns the string, with after processing the following backslash escape sequences. 1525 | * 1526 | * attacklab: The polite way to do this is with the new escapeCharacters() function: 1527 | * 1528 | * text = escapeCharacters(text,"\\",true); 1529 | * text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); 1530 | * 1531 | * ...but we're sidestepping its use of the (slow) RegExp constructor 1532 | * as an optimization for Firefox. This function gets called a LOT. 1533 | */ 1534 | showdown.subParser('encodeBackslashEscapes', function (text) { 1535 | 'use strict'; 1536 | text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback); 1537 | text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, showdown.helper.escapeCharactersCallback); 1538 | return text; 1539 | }); 1540 | 1541 | /** 1542 | * Encode/escape certain characters inside Markdown code runs. 1543 | * The point is that in code, these characters are literals, 1544 | * and lose their special Markdown meanings. 1545 | */ 1546 | showdown.subParser('encodeCode', function (text) { 1547 | 'use strict'; 1548 | 1549 | // Encode all ampersands; HTML entities are not 1550 | // entities within a Markdown code span. 1551 | text = text.replace(/&/g, '&'); 1552 | 1553 | // Do the angle bracket song and dance: 1554 | text = text.replace(//g, '>'); 1556 | 1557 | // Now, escape characters that are magic in Markdown: 1558 | text = showdown.helper.escapeCharacters(text, '*_{}[]\\', false); 1559 | 1560 | // jj the line above breaks this: 1561 | //--- 1562 | //* Item 1563 | // 1. Subitem 1564 | // special char: * 1565 | // --- 1566 | 1567 | return text; 1568 | }); 1569 | 1570 | /** 1571 | * Input: an email address, e.g. "foo@example.com" 1572 | * 1573 | * Output: the email address as a mailto link, with each character 1574 | * of the address encoded as either a decimal or hex entity, in 1575 | * the hopes of foiling most address harvesting spam bots. E.g.: 1576 | * 1577 | * foo 1579 | * @example.com 1580 | * 1581 | * Based on a filter by Matthew Wickline, posted to the BBEdit-Talk 1582 | * mailing list: 1583 | * 1584 | */ 1585 | showdown.subParser('encodeEmailAddress', function (addr) { 1586 | 'use strict'; 1587 | 1588 | var encode = [ 1589 | function (ch) { 1590 | return '&#' + ch.charCodeAt(0) + ';'; 1591 | }, 1592 | function (ch) { 1593 | return '&#x' + ch.charCodeAt(0).toString(16) + ';'; 1594 | }, 1595 | function (ch) { 1596 | return ch; 1597 | } 1598 | ]; 1599 | 1600 | addr = 'mailto:' + addr; 1601 | 1602 | addr = addr.replace(/./g, function (ch) { 1603 | if (ch === '@') { 1604 | // this *must* be encoded. I insist. 1605 | ch = encode[Math.floor(Math.random() * 2)](ch); 1606 | } else if (ch !== ':') { 1607 | // leave ':' alone (to spot mailto: later) 1608 | var r = Math.random(); 1609 | // roughly 10% raw, 45% hex, 45% dec 1610 | ch = ( 1611 | r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch) 1612 | ); 1613 | } 1614 | return ch; 1615 | }); 1616 | 1617 | addr = '' + addr + ''; 1618 | addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part 1619 | 1620 | return addr; 1621 | }); 1622 | 1623 | /** 1624 | * Within tags -- meaning between < and > -- encode [\ ` * _] so they 1625 | * don't conflict with their use in Markdown for code, italics and strong. 1626 | */ 1627 | showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text) { 1628 | 'use strict'; 1629 | 1630 | // Build a regex to find HTML tags and comments. See Friedl's 1631 | // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. 1632 | var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; 1633 | 1634 | text = text.replace(regex, function (wholeMatch) { 1635 | var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`'); 1636 | tag = showdown.helper.escapeCharacters(tag, '\\`*_', false); 1637 | return tag; 1638 | }); 1639 | 1640 | return text; 1641 | }); 1642 | 1643 | /** 1644 | * Handle github codeblocks prior to running HashHTML so that 1645 | * HTML contained within the codeblock gets escaped properly 1646 | * Example: 1647 | * ```ruby 1648 | * def hello_world(x) 1649 | * puts "Hello, #{x}" 1650 | * end 1651 | * ``` 1652 | */ 1653 | showdown.subParser('githubCodeBlocks', function (text, options, globals) { 1654 | 'use strict'; 1655 | 1656 | // early exit if option is not enabled 1657 | if (!options.ghCodeBlocks) { 1658 | return text; 1659 | } 1660 | 1661 | text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals); 1662 | 1663 | text += '~0'; 1664 | 1665 | text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) { 1666 | var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n'; 1667 | 1668 | // First parse the github code block 1669 | codeblock = showdown.subParser('encodeCode')(codeblock); 1670 | codeblock = showdown.subParser('detab')(codeblock); 1671 | codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines 1672 | codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace 1673 | 1674 | codeblock = '
' + codeblock + end + '
'; 1675 | 1676 | codeblock = showdown.subParser('hashBlock')(codeblock, options, globals); 1677 | 1678 | // Since GHCodeblocks can be false positives, we need to 1679 | // store the primitive text and the parsed text in a global var, 1680 | // and then return a token 1681 | return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n'; 1682 | }); 1683 | 1684 | // attacklab: strip sentinel 1685 | text = text.replace(/~0/, ''); 1686 | 1687 | return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals); 1688 | }); 1689 | 1690 | showdown.subParser('hashBlock', function (text, options, globals) { 1691 | 'use strict'; 1692 | text = text.replace(/(^\n+|\n+$)/g, ''); 1693 | return '\n\n~K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n'; 1694 | }); 1695 | 1696 | showdown.subParser('hashElement', function (text, options, globals) { 1697 | 'use strict'; 1698 | 1699 | return function (wholeMatch, m1) { 1700 | var blockText = m1; 1701 | 1702 | // Undo double lines 1703 | blockText = blockText.replace(/\n\n/g, '\n'); 1704 | blockText = blockText.replace(/^\n/, ''); 1705 | 1706 | // strip trailing blank lines 1707 | blockText = blockText.replace(/\n+$/g, ''); 1708 | 1709 | // Replace the element text with a marker ("~KxK" where x is its key) 1710 | blockText = '\n\n~K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n'; 1711 | 1712 | return blockText; 1713 | }; 1714 | }); 1715 | 1716 | showdown.subParser('hashHTMLBlocks', function (text, options, globals) { 1717 | 'use strict'; 1718 | 1719 | var blockTags = [ 1720 | 'pre', 1721 | 'div', 1722 | 'h1', 1723 | 'h2', 1724 | 'h3', 1725 | 'h4', 1726 | 'h5', 1727 | 'h6', 1728 | 'blockquote', 1729 | 'table', 1730 | 'dl', 1731 | 'ol', 1732 | 'ul', 1733 | 'script', 1734 | 'noscript', 1735 | 'form', 1736 | 'fieldset', 1737 | 'iframe', 1738 | 'math', 1739 | 'style', 1740 | 'section', 1741 | 'header', 1742 | 'footer', 1743 | 'nav', 1744 | 'article', 1745 | 'aside', 1746 | 'address', 1747 | 'audio', 1748 | 'canvas', 1749 | 'figure', 1750 | 'hgroup', 1751 | 'output', 1752 | 'video', 1753 | 'p' 1754 | ], 1755 | repFunc = function (wholeMatch, match, left, right) { 1756 | var txt = wholeMatch; 1757 | // check if this html element is marked as markdown 1758 | // if so, it's contents should be parsed as markdown 1759 | if (left.search(/\bmarkdown\b/) !== -1) { 1760 | txt = left + globals.converter.makeHtml(match) + right; 1761 | } 1762 | return '\n\n~K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n'; 1763 | }; 1764 | 1765 | for (var i = 0; i < blockTags.length; ++i) { 1766 | text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}<' + blockTags[i] + '\\b[^>]*>', '', 'gim'); 1767 | } 1768 | 1769 | // HR SPECIAL CASE 1770 | text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, 1771 | showdown.subParser('hashElement')(text, options, globals)); 1772 | 1773 | // Special case for standalone HTML comments: 1774 | text = text.replace(/()/g, 1775 | showdown.subParser('hashElement')(text, options, globals)); 1776 | 1777 | // PHP and ASP-style processor instructions ( and <%...%>) 1778 | text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, 1779 | showdown.subParser('hashElement')(text, options, globals)); 1780 | return text; 1781 | }); 1782 | 1783 | /** 1784 | * Hash span elements that should not be parsed as markdown 1785 | */ 1786 | showdown.subParser('hashHTMLSpans', function (text, config, globals) { 1787 | 'use strict'; 1788 | 1789 | var matches = showdown.helper.matchRecursiveRegExp(text, ']*>', '', 'gi'); 1790 | 1791 | for (var i = 0; i < matches.length; ++i) { 1792 | text = text.replace(matches[i][0], '~L' + (globals.gHtmlSpans.push(matches[i][0]) - 1) + 'L'); 1793 | } 1794 | return text; 1795 | }); 1796 | 1797 | /** 1798 | * Unhash HTML spans 1799 | */ 1800 | showdown.subParser('unhashHTMLSpans', function (text, config, globals) { 1801 | 'use strict'; 1802 | 1803 | for (var i = 0; i < globals.gHtmlSpans.length; ++i) { 1804 | text = text.replace('~L' + i + 'L', globals.gHtmlSpans[i]); 1805 | } 1806 | 1807 | return text; 1808 | }); 1809 | 1810 | /** 1811 | * Hash span elements that should not be parsed as markdown 1812 | */ 1813 | showdown.subParser('hashPreCodeTags', function (text, config, globals) { 1814 | 'use strict'; 1815 | 1816 | var repFunc = function (wholeMatch, match, left, right) { 1817 | // encode html entities 1818 | var codeblock = left + showdown.subParser('encodeCode')(match) + right; 1819 | return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n'; 1820 | }; 1821 | 1822 | text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}]*>\\s*]*>', '^(?: |\\t){0,3}\\s*
', 'gim'); 1823 | return text; 1824 | }); 1825 | 1826 | showdown.subParser('headers', function (text, options, globals) { 1827 | 'use strict'; 1828 | 1829 | text = globals.converter._dispatch('headers.before', text, options, globals); 1830 | 1831 | var prefixHeader = options.prefixHeaderId, 1832 | headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart), 1833 | 1834 | // Set text-style headers: 1835 | // Header 1 1836 | // ======== 1837 | // 1838 | // Header 2 1839 | // -------- 1840 | // 1841 | setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm, 1842 | setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm; 1843 | 1844 | text = text.replace(setextRegexH1, function (wholeMatch, m1) { 1845 | 1846 | var spanGamut = showdown.subParser('spanGamut')(m1, options, globals), 1847 | hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"', 1848 | hLevel = headerLevelStart, 1849 | hashBlock = '' + spanGamut + ''; 1850 | return showdown.subParser('hashBlock')(hashBlock, options, globals); 1851 | }); 1852 | 1853 | text = text.replace(setextRegexH2, function (matchFound, m1) { 1854 | var spanGamut = showdown.subParser('spanGamut')(m1, options, globals), 1855 | hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"', 1856 | hLevel = headerLevelStart + 1, 1857 | hashBlock = '' + spanGamut + ''; 1858 | return showdown.subParser('hashBlock')(hashBlock, options, globals); 1859 | }); 1860 | 1861 | // atx-style headers: 1862 | // # Header 1 1863 | // ## Header 2 1864 | // ## Header 2 with closing hashes ## 1865 | // ... 1866 | // ###### Header 6 1867 | // 1868 | text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, function (wholeMatch, m1, m2) { 1869 | var span = showdown.subParser('spanGamut')(m2, options, globals), 1870 | hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"', 1871 | hLevel = headerLevelStart - 1 + m1.length, 1872 | header = '' + span + ''; 1873 | 1874 | return showdown.subParser('hashBlock')(header, options, globals); 1875 | }); 1876 | 1877 | function headerId(m) { 1878 | var title, escapedId = m.replace(/[^\w]/g, '').toLowerCase(); 1879 | 1880 | if (globals.hashLinkCounts[escapedId]) { 1881 | title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++); 1882 | } else { 1883 | title = escapedId; 1884 | globals.hashLinkCounts[escapedId] = 1; 1885 | } 1886 | 1887 | // Prefix id to prevent causing inadvertent pre-existing style matches. 1888 | if (prefixHeader === true) { 1889 | prefixHeader = 'section'; 1890 | } 1891 | 1892 | if (showdown.helper.isString(prefixHeader)) { 1893 | return prefixHeader + title; 1894 | } 1895 | return title; 1896 | } 1897 | 1898 | text = globals.converter._dispatch('headers.after', text, options, globals); 1899 | return text; 1900 | }); 1901 | 1902 | /** 1903 | * Turn Markdown image shortcuts into tags. 1904 | */ 1905 | showdown.subParser('images', function (text, options, globals) { 1906 | 'use strict'; 1907 | 1908 | text = globals.converter._dispatch('images.before', text, options, globals); 1909 | 1910 | var inlineRegExp = /!\[(.*?)]\s?\([ \t]*()?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g, 1911 | referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g; 1912 | 1913 | function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) { 1914 | 1915 | var gUrls = globals.gUrls, 1916 | gTitles = globals.gTitles, 1917 | gDims = globals.gDimensions; 1918 | 1919 | linkId = linkId.toLowerCase(); 1920 | 1921 | if (!title) { 1922 | title = ''; 1923 | } 1924 | 1925 | if (url === '' || url === null) { 1926 | if (linkId === '' || linkId === null) { 1927 | // lower-case and turn embedded newlines into spaces 1928 | linkId = altText.toLowerCase().replace(/ ?\n/g, ' '); 1929 | } 1930 | url = '#' + linkId; 1931 | 1932 | if (!showdown.helper.isUndefined(gUrls[linkId])) { 1933 | url = gUrls[linkId]; 1934 | if (!showdown.helper.isUndefined(gTitles[linkId])) { 1935 | title = gTitles[linkId]; 1936 | } 1937 | if (!showdown.helper.isUndefined(gDims[linkId])) { 1938 | width = gDims[linkId].width; 1939 | height = gDims[linkId].height; 1940 | } 1941 | } else { 1942 | return wholeMatch; 1943 | } 1944 | } 1945 | 1946 | altText = altText.replace(/"/g, '"'); 1947 | altText = showdown.helper.escapeCharacters(altText, '*_', false); 1948 | url = showdown.helper.escapeCharacters(url, '*_', false); 1949 | var result = '' + altText + 'x "optional title") 1973 | text = text.replace(inlineRegExp, writeImageTag); 1974 | 1975 | text = globals.converter._dispatch('images.after', text, options, globals); 1976 | return text; 1977 | }); 1978 | 1979 | showdown.subParser('italicsAndBold', function (text, options, globals) { 1980 | 'use strict'; 1981 | 1982 | text = globals.converter._dispatch('italicsAndBold.before', text, options, globals); 1983 | 1984 | if (options.literalMidWordUnderscores) { 1985 | //underscores 1986 | // Since we are consuming a \s character, we need to add it 1987 | text = text.replace(/(^|\s|>|\b)__(?=\S)([\s\S]+?)__(?=\b|<|\s|$)/gm, '$1$2'); 1988 | text = text.replace(/(^|\s|>|\b)_(?=\S)([\s\S]+?)_(?=\b|<|\s|$)/gm, '$1$2'); 1989 | //asterisks 1990 | text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, '$2'); 1991 | text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, '$2'); 1992 | 1993 | } else { 1994 | // must go first: 1995 | text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '$2'); 1996 | text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '$2'); 1997 | } 1998 | 1999 | text = globals.converter._dispatch('italicsAndBold.after', text, options, globals); 2000 | return text; 2001 | }); 2002 | 2003 | /** 2004 | * Form HTML ordered (numbered) and unordered (bulleted) lists. 2005 | */ 2006 | showdown.subParser('lists', function (text, options, globals) { 2007 | 'use strict'; 2008 | 2009 | text = globals.converter._dispatch('lists.before', text, options, globals); 2010 | /** 2011 | * Process the contents of a single ordered or unordered list, splitting it 2012 | * into individual list items. 2013 | * @param {string} listStr 2014 | * @param {boolean} trimTrailing 2015 | * @returns {string} 2016 | */ 2017 | function processListItems (listStr, trimTrailing) { 2018 | // The $g_list_level global keeps track of when we're inside a list. 2019 | // Each time we enter a list, we increment it; when we leave a list, 2020 | // we decrement. If it's zero, we're not in a list anymore. 2021 | // 2022 | // We do this because when we're not inside a list, we want to treat 2023 | // something like this: 2024 | // 2025 | // I recommend upgrading to version 2026 | // 8. Oops, now this line is treated 2027 | // as a sub-list. 2028 | // 2029 | // As a single paragraph, despite the fact that the second line starts 2030 | // with a digit-period-space sequence. 2031 | // 2032 | // Whereas when we're inside a list (or sub-list), that line will be 2033 | // treated as the start of a sub-list. What a kludge, huh? This is 2034 | // an aspect of Markdown's syntax that's hard to parse perfectly 2035 | // without resorting to mind-reading. Perhaps the solution is to 2036 | // change the syntax rules such that sub-lists must start with a 2037 | // starting cardinal number; e.g. "1." or "a.". 2038 | globals.gListLevel++; 2039 | 2040 | // trim trailing blank lines: 2041 | listStr = listStr.replace(/\n{2,}$/, '\n'); 2042 | 2043 | // attacklab: add sentinel to emulate \z 2044 | listStr += '~0'; 2045 | 2046 | var rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, 2047 | isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr)); 2048 | 2049 | listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) { 2050 | checked = (checked && checked.trim() !== ''); 2051 | var item = showdown.subParser('outdent')(m4, options, globals), 2052 | bulletStyle = ''; 2053 | 2054 | // Support for github tasklists 2055 | if (taskbtn && options.tasklists) { 2056 | bulletStyle = ' class="task-list-item" style="list-style-type: none;"'; 2057 | item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () { 2058 | var otp = ' -1)) { 2070 | item = showdown.subParser('githubCodeBlocks')(item, options, globals); 2071 | item = showdown.subParser('blockGamut')(item, options, globals); 2072 | } else { 2073 | // Recursion for sub-lists: 2074 | item = showdown.subParser('lists')(item, options, globals); 2075 | item = item.replace(/\n$/, ''); // chomp(item) 2076 | if (isParagraphed) { 2077 | item = showdown.subParser('paragraphs')(item, options, globals); 2078 | } else { 2079 | item = showdown.subParser('spanGamut')(item, options, globals); 2080 | } 2081 | } 2082 | item = '\n' + item + '\n'; 2083 | return item; 2084 | }); 2085 | 2086 | // attacklab: strip sentinel 2087 | listStr = listStr.replace(/~0/g, ''); 2088 | 2089 | globals.gListLevel--; 2090 | 2091 | if (trimTrailing) { 2092 | listStr = listStr.replace(/\s+$/, ''); 2093 | } 2094 | 2095 | return listStr; 2096 | } 2097 | 2098 | /** 2099 | * Check and parse consecutive lists (better fix for issue #142) 2100 | * @param {string} list 2101 | * @param {string} listType 2102 | * @param {boolean} trimTrailing 2103 | * @returns {string} 2104 | */ 2105 | function parseConsecutiveLists(list, listType, trimTrailing) { 2106 | // check if we caught 2 or more consecutive lists by mistake 2107 | // we use the counterRgx, meaning if listType is UL we look for UL and vice versa 2108 | var counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm, 2109 | subLists = [], 2110 | result = ''; 2111 | 2112 | if (list.search(counterRxg) !== -1) { 2113 | (function parseCL(txt) { 2114 | var pos = txt.search(counterRxg); 2115 | if (pos !== -1) { 2116 | // slice 2117 | result += '\n\n<' + listType + '>' + processListItems(txt.slice(0, pos), !!trimTrailing) + '\n\n'; 2118 | 2119 | // invert counterType and listType 2120 | listType = (listType === 'ul') ? 'ol' : 'ul'; 2121 | counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm; 2122 | 2123 | //recurse 2124 | parseCL(txt.slice(pos)); 2125 | } else { 2126 | result += '\n\n<' + listType + '>' + processListItems(txt, !!trimTrailing) + '\n\n'; 2127 | } 2128 | })(list); 2129 | for (var i = 0; i < subLists.length; ++i) { 2130 | 2131 | } 2132 | } else { 2133 | result = '\n\n<' + listType + '>' + processListItems(list, !!trimTrailing) + '\n\n'; 2134 | } 2135 | 2136 | return result; 2137 | } 2138 | 2139 | // attacklab: add sentinel to hack around khtml/safari bug: 2140 | // http://bugs.webkit.org/show_bug.cgi?id=11231 2141 | text += '~0'; 2142 | 2143 | // Re-usable pattern to match any entire ul or ol list: 2144 | var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; 2145 | 2146 | if (globals.gListLevel) { 2147 | text = text.replace(wholeList, function (wholeMatch, list, m2) { 2148 | var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; 2149 | return parseConsecutiveLists(list, listType, true); 2150 | }); 2151 | } else { 2152 | wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; 2153 | //wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g; 2154 | text = text.replace(wholeList, function (wholeMatch, m1, list, m3) { 2155 | 2156 | var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; 2157 | return parseConsecutiveLists(list, listType); 2158 | }); 2159 | } 2160 | 2161 | // attacklab: strip sentinel 2162 | text = text.replace(/~0/, ''); 2163 | 2164 | text = globals.converter._dispatch('lists.after', text, options, globals); 2165 | return text; 2166 | }); 2167 | 2168 | /** 2169 | * Remove one level of line-leading tabs or spaces 2170 | */ 2171 | showdown.subParser('outdent', function (text) { 2172 | 'use strict'; 2173 | 2174 | // attacklab: hack around Konqueror 3.5.4 bug: 2175 | // "----------bug".replace(/^-/g,"") == "bug" 2176 | text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width 2177 | 2178 | // attacklab: clean up hack 2179 | text = text.replace(/~0/g, ''); 2180 | 2181 | return text; 2182 | }); 2183 | 2184 | /** 2185 | * 2186 | */ 2187 | showdown.subParser('paragraphs', function (text, options, globals) { 2188 | 'use strict'; 2189 | 2190 | text = globals.converter._dispatch('paragraphs.before', text, options, globals); 2191 | // Strip leading and trailing lines: 2192 | text = text.replace(/^\n+/g, ''); 2193 | text = text.replace(/\n+$/g, ''); 2194 | 2195 | var grafs = text.split(/\n{2,}/g), 2196 | grafsOut = [], 2197 | end = grafs.length; // Wrap

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

'); 2207 | str += '

'; 2208 | grafsOut.push(str); 2209 | } 2210 | } 2211 | 2212 | /** Unhashify HTML blocks */ 2213 | end = grafsOut.length; 2214 | for (i = 0; i < end; i++) { 2215 | var blockText = '', 2216 | grafsOutIt = grafsOut[i], 2217 | codeFlag = false; 2218 | // if this is a marker for an html block... 2219 | while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) { 2220 | var delim = RegExp.$1, 2221 | num = RegExp.$2; 2222 | 2223 | if (delim === 'K') { 2224 | blockText = globals.gHtmlBlocks[num]; 2225 | } else { 2226 | // we need to check if ghBlock is a false positive 2227 | if (codeFlag) { 2228 | // use encoded version of all text 2229 | blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text); 2230 | } else { 2231 | blockText = globals.ghCodeBlocks[num].codeblock; 2232 | } 2233 | } 2234 | blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs 2235 | 2236 | grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText); 2237 | // Check if grafsOutIt is a pre->code 2238 | if (/^]*>\s*]*>/.test(grafsOutIt)) { 2239 | codeFlag = true; 2240 | } 2241 | } 2242 | grafsOut[i] = grafsOutIt; 2243 | } 2244 | text = grafsOut.join('\n\n'); 2245 | // Strip leading and trailing lines: 2246 | text = text.replace(/^\n+/g, ''); 2247 | text = text.replace(/\n+$/g, ''); 2248 | return globals.converter._dispatch('paragraphs.after', text, options, globals); 2249 | }); 2250 | 2251 | /** 2252 | * Run extension 2253 | */ 2254 | showdown.subParser('runExtension', function (ext, text, options, globals) { 2255 | 'use strict'; 2256 | 2257 | if (ext.filter) { 2258 | text = ext.filter(text, globals.converter, options); 2259 | 2260 | } else if (ext.regex) { 2261 | // TODO remove this when old extension loading mechanism is deprecated 2262 | var re = ext.regex; 2263 | if (!re instanceof RegExp) { 2264 | re = new RegExp(re, 'g'); 2265 | } 2266 | text = text.replace(re, ext.replace); 2267 | } 2268 | 2269 | return text; 2270 | }); 2271 | 2272 | /** 2273 | * These are all the transformations that occur *within* block-level 2274 | * tags like paragraphs, headers, and list items. 2275 | */ 2276 | showdown.subParser('spanGamut', function (text, options, globals) { 2277 | 'use strict'; 2278 | 2279 | text = globals.converter._dispatch('spanGamut.before', text, options, globals); 2280 | text = showdown.subParser('codeSpans')(text, options, globals); 2281 | text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals); 2282 | text = showdown.subParser('encodeBackslashEscapes')(text, options, globals); 2283 | 2284 | // Process anchor and image tags. Images must come first, 2285 | // because ![foo][f] looks like an anchor. 2286 | text = showdown.subParser('images')(text, options, globals); 2287 | text = showdown.subParser('anchors')(text, options, globals); 2288 | 2289 | // Make links out of things like `` 2290 | // Must come after _DoAnchors(), because you can use < and > 2291 | // delimiters in inline links like [this](). 2292 | text = showdown.subParser('autoLinks')(text, options, globals); 2293 | text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals); 2294 | text = showdown.subParser('italicsAndBold')(text, options, globals); 2295 | text = showdown.subParser('strikethrough')(text, options, globals); 2296 | 2297 | // Do hard breaks: 2298 | text = text.replace(/ +\n/g, '
\n'); 2299 | 2300 | text = globals.converter._dispatch('spanGamut.after', text, options, globals); 2301 | return text; 2302 | }); 2303 | 2304 | showdown.subParser('strikethrough', function (text, options, globals) { 2305 | 'use strict'; 2306 | 2307 | if (options.strikethrough) { 2308 | text = globals.converter._dispatch('strikethrough.before', text, options, globals); 2309 | text = text.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g, '$1'); 2310 | text = globals.converter._dispatch('strikethrough.after', text, options, globals); 2311 | } 2312 | 2313 | return text; 2314 | }); 2315 | 2316 | /** 2317 | * Strip any lines consisting only of spaces and tabs. 2318 | * This makes subsequent regexs easier to write, because we can 2319 | * match consecutive blank lines with /\n+/ instead of something 2320 | * contorted like /[ \t]*\n+/ 2321 | */ 2322 | showdown.subParser('stripBlankLines', function (text) { 2323 | 'use strict'; 2324 | return text.replace(/^[ \t]+$/mg, ''); 2325 | }); 2326 | 2327 | /** 2328 | * Strips link definitions from text, stores the URLs and titles in 2329 | * hash references. 2330 | * Link defs are in the form: ^[id]: url "optional title" 2331 | * 2332 | * ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 2333 | * [ \t]* 2334 | * \n? // maybe *one* newline 2335 | * [ \t]* 2336 | * ? // url = $2 2337 | * [ \t]* 2338 | * \n? // maybe one newline 2339 | * [ \t]* 2340 | * (?: 2341 | * (\n*) // any lines skipped = $3 attacklab: lookbehind removed 2342 | * ["(] 2343 | * (.+?) // title = $4 2344 | * [")] 2345 | * [ \t]* 2346 | * )? // title is optional 2347 | * (?:\n+|$) 2348 | * /gm, 2349 | * function(){...}); 2350 | * 2351 | */ 2352 | showdown.subParser('stripLinkDefinitions', function (text, options, globals) { 2353 | 'use strict'; 2354 | 2355 | 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; 2356 | 2357 | // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug 2358 | text += '~0'; 2359 | 2360 | text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) { 2361 | linkId = linkId.toLowerCase(); 2362 | globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url); // Link IDs are case-insensitive 2363 | 2364 | if (blankLines) { 2365 | // Oops, found blank lines, so it's not a title. 2366 | // Put back the parenthetical statement we stole. 2367 | return blankLines + title; 2368 | 2369 | } else { 2370 | if (title) { 2371 | globals.gTitles[linkId] = title.replace(/"|'/g, '"'); 2372 | } 2373 | if (options.parseImgDimensions && width && height) { 2374 | globals.gDimensions[linkId] = { 2375 | width: width, 2376 | height: height 2377 | }; 2378 | } 2379 | } 2380 | // Completely remove the definition from the text 2381 | return ''; 2382 | }); 2383 | 2384 | // attacklab: strip sentinel 2385 | text = text.replace(/~0/, ''); 2386 | 2387 | return text; 2388 | }); 2389 | 2390 | showdown.subParser('tables', function (text, options, globals) { 2391 | 'use strict'; 2392 | 2393 | if (!options.tables) { 2394 | return text; 2395 | } 2396 | 2397 | var tableRgx = /^[ \t]{0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|~0)/gm; 2398 | 2399 | function parseStyles(sLine) { 2400 | if (/^:[ \t]*--*$/.test(sLine)) { 2401 | return ' style="text-align:left;"'; 2402 | } else if (/^--*[ \t]*:[ \t]*$/.test(sLine)) { 2403 | return ' style="text-align:right;"'; 2404 | } else if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) { 2405 | return ' style="text-align:center;"'; 2406 | } else { 2407 | return ''; 2408 | } 2409 | } 2410 | 2411 | function parseHeaders(header, style) { 2412 | var id = ''; 2413 | header = header.trim(); 2414 | if (options.tableHeaderId) { 2415 | id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"'; 2416 | } 2417 | header = showdown.subParser('spanGamut')(header, options, globals); 2418 | 2419 | return '' + header + '\n'; 2420 | } 2421 | 2422 | function parseCells(cell, style) { 2423 | var subText = showdown.subParser('spanGamut')(cell, options, globals); 2424 | return '' + subText + '\n'; 2425 | } 2426 | 2427 | function buildTable(headers, cells) { 2428 | var tb = '\n\n\n', 2429 | tblLgn = headers.length; 2430 | 2431 | for (var i = 0; i < tblLgn; ++i) { 2432 | tb += headers[i]; 2433 | } 2434 | tb += '\n\n\n'; 2435 | 2436 | for (i = 0; i < cells.length; ++i) { 2437 | tb += '\n'; 2438 | for (var ii = 0; ii < tblLgn; ++ii) { 2439 | tb += cells[i][ii]; 2440 | } 2441 | tb += '\n'; 2442 | } 2443 | tb += '\n
\n'; 2444 | return tb; 2445 | } 2446 | 2447 | text = globals.converter._dispatch('tables.before', text, options, globals); 2448 | 2449 | text = text.replace(tableRgx, function (rawTable) { 2450 | 2451 | var i, tableLines = rawTable.split('\n'); 2452 | 2453 | // strip wrong first and last column if wrapped tables are used 2454 | for (i = 0; i < tableLines.length; ++i) { 2455 | if (/^[ \t]{0,3}\|/.test(tableLines[i])) { 2456 | tableLines[i] = tableLines[i].replace(/^[ \t]{0,3}\|/, ''); 2457 | } 2458 | if (/\|[ \t]*$/.test(tableLines[i])) { 2459 | tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, ''); 2460 | } 2461 | } 2462 | 2463 | var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}), 2464 | rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}), 2465 | rawCells = [], 2466 | headers = [], 2467 | styles = [], 2468 | cells = []; 2469 | 2470 | tableLines.shift(); 2471 | tableLines.shift(); 2472 | 2473 | for (i = 0; i < tableLines.length; ++i) { 2474 | if (tableLines[i].trim() === '') { 2475 | continue; 2476 | } 2477 | rawCells.push( 2478 | tableLines[i] 2479 | .split('|') 2480 | .map(function (s) { 2481 | return s.trim(); 2482 | }) 2483 | ); 2484 | } 2485 | 2486 | if (rawHeaders.length < rawStyles.length) { 2487 | return rawTable; 2488 | } 2489 | 2490 | for (i = 0; i < rawStyles.length; ++i) { 2491 | styles.push(parseStyles(rawStyles[i])); 2492 | } 2493 | 2494 | for (i = 0; i < rawHeaders.length; ++i) { 2495 | if (showdown.helper.isUndefined(styles[i])) { 2496 | styles[i] = ''; 2497 | } 2498 | headers.push(parseHeaders(rawHeaders[i], styles[i])); 2499 | } 2500 | 2501 | for (i = 0; i < rawCells.length; ++i) { 2502 | var row = []; 2503 | for (var ii = 0; ii < headers.length; ++ii) { 2504 | if (showdown.helper.isUndefined(rawCells[i][ii])) { 2505 | 2506 | } 2507 | row.push(parseCells(rawCells[i][ii], styles[ii])); 2508 | } 2509 | cells.push(row); 2510 | } 2511 | 2512 | return buildTable(headers, cells); 2513 | }); 2514 | 2515 | text = globals.converter._dispatch('tables.after', text, options, globals); 2516 | 2517 | return text; 2518 | }); 2519 | 2520 | /** 2521 | * Swap back in all the special characters we've hidden. 2522 | */ 2523 | showdown.subParser('unescapeSpecialChars', function (text) { 2524 | 'use strict'; 2525 | 2526 | text = text.replace(/~E(\d+)E/g, function (wholeMatch, m1) { 2527 | var charCodeToReplace = parseInt(m1); 2528 | return String.fromCharCode(charCodeToReplace); 2529 | }); 2530 | return text; 2531 | }); 2532 | module.exports = showdown; 2533 | -------------------------------------------------------------------------------- /utils/wxParse/wxDiscode.js: -------------------------------------------------------------------------------- 1 | // HTML 支持的数学符号 2 | function strNumDiscode(str){ 3 | str = str.replace(/∀/g, '∀'); 4 | str = str.replace(/∂/g, '∂'); 5 | str = str.replace(/&exists;/g, '∃'); 6 | str = str.replace(/∅/g, '∅'); 7 | str = str.replace(/∇/g, '∇'); 8 | str = str.replace(/∈/g, '∈'); 9 | str = str.replace(/∉/g, '∉'); 10 | str = str.replace(/∋/g, '∋'); 11 | str = str.replace(/∏/g, '∏'); 12 | str = str.replace(/∑/g, '∑'); 13 | str = str.replace(/−/g, '−'); 14 | str = str.replace(/∗/g, '∗'); 15 | str = str.replace(/√/g, '√'); 16 | str = str.replace(/∝/g, '∝'); 17 | str = str.replace(/∞/g, '∞'); 18 | str = str.replace(/∠/g, '∠'); 19 | str = str.replace(/∧/g, '∧'); 20 | str = str.replace(/∨/g, '∨'); 21 | str = str.replace(/∩/g, '∩'); 22 | str = str.replace(/∩/g, '∪'); 23 | str = str.replace(/∫/g, '∫'); 24 | str = str.replace(/∴/g, '∴'); 25 | str = str.replace(/∼/g, '∼'); 26 | str = str.replace(/≅/g, '≅'); 27 | str = str.replace(/≈/g, '≈'); 28 | str = str.replace(/≠/g, '≠'); 29 | str = str.replace(/≤/g, '≤'); 30 | str = str.replace(/≥/g, '≥'); 31 | str = str.replace(/⊂/g, '⊂'); 32 | str = str.replace(/⊃/g, '⊃'); 33 | str = str.replace(/⊄/g, '⊄'); 34 | str = str.replace(/⊆/g, '⊆'); 35 | str = str.replace(/⊇/g, '⊇'); 36 | str = str.replace(/⊕/g, '⊕'); 37 | str = str.replace(/⊗/g, '⊗'); 38 | str = str.replace(/⊥/g, '⊥'); 39 | str = str.replace(/⋅/g, '⋅'); 40 | return str; 41 | } 42 | 43 | //HTML 支持的希腊字母 44 | function strGreeceDiscode(str){ 45 | str = str.replace(/Α/g, 'Α'); 46 | str = str.replace(/Β/g, 'Β'); 47 | str = str.replace(/Γ/g, 'Γ'); 48 | str = str.replace(/Δ/g, 'Δ'); 49 | str = str.replace(/Ε/g, 'Ε'); 50 | str = str.replace(/Ζ/g, 'Ζ'); 51 | str = str.replace(/Η/g, 'Η'); 52 | str = str.replace(/Θ/g, 'Θ'); 53 | str = str.replace(/Ι/g, 'Ι'); 54 | str = str.replace(/Κ/g, 'Κ'); 55 | str = str.replace(/Λ/g, 'Λ'); 56 | str = str.replace(/Μ/g, 'Μ'); 57 | str = str.replace(/Ν/g, 'Ν'); 58 | str = str.replace(/Ξ/g, 'Ν'); 59 | str = str.replace(/Ο/g, 'Ο'); 60 | str = str.replace(/Π/g, 'Π'); 61 | str = str.replace(/Ρ/g, 'Ρ'); 62 | str = str.replace(/Σ/g, 'Σ'); 63 | str = str.replace(/Τ/g, 'Τ'); 64 | str = str.replace(/Υ/g, 'Υ'); 65 | str = str.replace(/Φ/g, 'Φ'); 66 | str = str.replace(/Χ/g, 'Χ'); 67 | str = str.replace(/Ψ/g, 'Ψ'); 68 | str = str.replace(/Ω/g, 'Ω'); 69 | 70 | str = str.replace(/α/g, 'α'); 71 | str = str.replace(/β/g, 'β'); 72 | str = str.replace(/γ/g, 'γ'); 73 | str = str.replace(/δ/g, 'δ'); 74 | str = str.replace(/ε/g, 'ε'); 75 | str = str.replace(/ζ/g, 'ζ'); 76 | str = str.replace(/η/g, 'η'); 77 | str = str.replace(/θ/g, 'θ'); 78 | str = str.replace(/ι/g, 'ι'); 79 | str = str.replace(/κ/g, 'κ'); 80 | str = str.replace(/λ/g, 'λ'); 81 | str = str.replace(/μ/g, 'μ'); 82 | str = str.replace(/ν/g, 'ν'); 83 | str = str.replace(/ξ/g, 'ξ'); 84 | str = str.replace(/ο/g, 'ο'); 85 | str = str.replace(/π/g, 'π'); 86 | str = str.replace(/ρ/g, 'ρ'); 87 | str = str.replace(/ς/g, 'ς'); 88 | str = str.replace(/σ/g, 'σ'); 89 | str = str.replace(/τ/g, 'τ'); 90 | str = str.replace(/υ/g, 'υ'); 91 | str = str.replace(/φ/g, 'φ'); 92 | str = str.replace(/χ/g, 'χ'); 93 | str = str.replace(/ψ/g, 'ψ'); 94 | str = str.replace(/ω/g, 'ω'); 95 | str = str.replace(/ϑ/g, 'ϑ'); 96 | str = str.replace(/ϒ/g, 'ϒ'); 97 | str = str.replace(/ϖ/g, 'ϖ'); 98 | str = str.replace(/·/g, '·'); 99 | return str; 100 | } 101 | 102 | // 103 | 104 | function strcharacterDiscode(str){ 105 | // 加入常用解析 106 | str = str.replace(/ /g, ' '); 107 | str = str.replace(/"/g, "'"); 108 | str = str.replace(/&/g, '&'); 109 | // str = str.replace(/</g, '‹'); 110 | // str = str.replace(/>/g, '›'); 111 | 112 | str = str.replace(/</g, '<'); 113 | str = str.replace(/>/g, '>'); 114 | str = str.replace(/•/g, '•'); 115 | 116 | return str; 117 | } 118 | 119 | // HTML 支持的其他实体 120 | function strOtherDiscode(str){ 121 | str = str.replace(/Œ/g, 'Œ'); 122 | str = str.replace(/œ/g, 'œ'); 123 | str = str.replace(/Š/g, 'Š'); 124 | str = str.replace(/š/g, 'š'); 125 | str = str.replace(/Ÿ/g, 'Ÿ'); 126 | str = str.replace(/ƒ/g, 'ƒ'); 127 | str = str.replace(/ˆ/g, 'ˆ'); 128 | str = str.replace(/˜/g, '˜'); 129 | str = str.replace(/ /g, ''); 130 | str = str.replace(/ /g, ''); 131 | str = str.replace(/ /g, ''); 132 | str = str.replace(/‌/g, ''); 133 | str = str.replace(/‍/g, ''); 134 | str = str.replace(/‎/g, ''); 135 | str = str.replace(/‏/g, ''); 136 | str = str.replace(/–/g, '–'); 137 | str = str.replace(/—/g, '—'); 138 | str = str.replace(/‘/g, '‘'); 139 | str = str.replace(/’/g, '’'); 140 | str = str.replace(/‚/g, '‚'); 141 | str = str.replace(/“/g, '“'); 142 | str = str.replace(/”/g, '”'); 143 | str = str.replace(/„/g, '„'); 144 | str = str.replace(/†/g, '†'); 145 | str = str.replace(/‡/g, '‡'); 146 | str = str.replace(/•/g, '•'); 147 | str = str.replace(/…/g, '…'); 148 | str = str.replace(/‰/g, '‰'); 149 | str = str.replace(/′/g, '′'); 150 | str = str.replace(/″/g, '″'); 151 | str = str.replace(/‹/g, '‹'); 152 | str = str.replace(/›/g, '›'); 153 | str = str.replace(/‾/g, '‾'); 154 | str = str.replace(/€/g, '€'); 155 | str = str.replace(/™/g, '™'); 156 | 157 | str = str.replace(/←/g, '←'); 158 | str = str.replace(/↑/g, '↑'); 159 | str = str.replace(/→/g, '→'); 160 | str = str.replace(/↓/g, '↓'); 161 | str = str.replace(/↔/g, '↔'); 162 | str = str.replace(/↵/g, '↵'); 163 | str = str.replace(/⌈/g, '⌈'); 164 | str = str.replace(/⌉/g, '⌉'); 165 | 166 | str = str.replace(/⌊/g, '⌊'); 167 | str = str.replace(/⌋/g, '⌋'); 168 | str = str.replace(/◊/g, '◊'); 169 | str = str.replace(/♠/g, '♠'); 170 | str = str.replace(/♣/g, '♣'); 171 | str = str.replace(/♥/g, '♥'); 172 | 173 | str = str.replace(/♦/g, '♦'); 174 | str = str.replace(/'/g, '\''); 175 | return str; 176 | } 177 | 178 | function strMoreDiscode(str){ 179 | str = str.replace(/\r\n/g,""); 180 | str = str.replace(/\n/g,""); 181 | 182 | str = str.replace(/code/g,"wxxxcode-style"); 183 | return str; 184 | } 185 | 186 | function strDiscode(str){ 187 | str = strNumDiscode(str); 188 | str = strGreeceDiscode(str); 189 | str = strcharacterDiscode(str); 190 | str = strOtherDiscode(str); 191 | str = strMoreDiscode(str); 192 | return str; 193 | } 194 | function urlToHttpUrl(url,rep){ 195 | 196 | var patt1 = new RegExp("^//"); 197 | var result = patt1.test(url); 198 | if(result){ 199 | url = rep+":"+url; 200 | } 201 | return url; 202 | } 203 | 204 | module.exports = { 205 | strDiscode:strDiscode, 206 | urlToHttpUrl:urlToHttpUrl 207 | } -------------------------------------------------------------------------------- /utils/wxParse/wxParse.js: -------------------------------------------------------------------------------- 1 | /** 2 | * author: Di (微信小程序开发工程师) 3 | * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) 4 | * 垂直微信小程序开发交流社区 5 | * 6 | * github地址: https://github.com/icindy/wxParse 7 | * 8 | * for: 微信小程序富文本解析 9 | * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 10 | */ 11 | 12 | /** 13 | * utils函数引入 14 | **/ 15 | import showdown from './showdown.js'; 16 | import HtmlToJson from './html2json.js'; 17 | /** 18 | * 配置及公有属性 19 | **/ 20 | var realWindowWidth = 0; 21 | var realWindowHeight = 0; 22 | wx.getSystemInfo({ 23 | success: function (res) { 24 | realWindowWidth = res.windowWidth 25 | realWindowHeight = res.windowHeight 26 | } 27 | }) 28 | /** 29 | * 主函数入口区 30 | **/ 31 | function wxParse(bindName = 'wxParseData', type='html', data='
数据不能为空
', target,imagePadding) { 32 | var that = target; 33 | var transData = {};//存放转化后的数据 34 | if (type == 'html') { 35 | transData = HtmlToJson.html2json(data, bindName); 36 | console.log(JSON.stringify(transData, ' ', ' ')); 37 | } else if (type == 'md' || type == 'markdown') { 38 | var converter = new showdown.Converter(); 39 | var html = converter.makeHtml(data); 40 | transData = HtmlToJson.html2json(html, bindName); 41 | console.log(JSON.stringify(transData, ' ', ' ')); 42 | } 43 | transData.view = {}; 44 | transData.view.imagePadding = 0; 45 | if(typeof(imagePadding) != 'undefined'){ 46 | transData.view.imagePadding = imagePadding 47 | } 48 | var bindData = {}; 49 | bindData[bindName] = transData; 50 | that.setData(bindData) 51 | that.wxParseImgLoad = wxParseImgLoad; 52 | that.wxParseImgTap = wxParseImgTap; 53 | } 54 | // 图片点击事件 55 | function wxParseImgTap(e) { 56 | var that = this; 57 | var nowImgUrl = e.target.dataset.src; 58 | var tagFrom = e.target.dataset.from; 59 | if (typeof (tagFrom) != 'undefined' && tagFrom.length > 0) { 60 | wx.previewImage({ 61 | current: nowImgUrl, // 当前显示图片的http链接 62 | urls: that.data[tagFrom].imageUrls // 需要预览的图片http链接列表 63 | }) 64 | } 65 | } 66 | 67 | /** 68 | * 图片视觉宽高计算函数区 69 | **/ 70 | function wxParseImgLoad(e) { 71 | var that = this; 72 | var tagFrom = e.target.dataset.from; 73 | var idx = e.target.dataset.idx; 74 | if (typeof (tagFrom) != 'undefined' && tagFrom.length > 0) { 75 | calMoreImageInfo(e, idx, that, tagFrom) 76 | } 77 | } 78 | // 假循环获取计算图片视觉最佳宽高 79 | function calMoreImageInfo(e, idx, that, bindName) { 80 | var temData = that.data[bindName]; 81 | if (!temData || temData.images.length == 0) { 82 | return; 83 | } 84 | var temImages = temData.images; 85 | //因为无法获取view宽度 需要自定义padding进行计算,稍后处理 86 | var recal = wxAutoImageCal(e.detail.width, e.detail.height,that,bindName); 87 | // temImages[idx].width = recal.imageWidth; 88 | // temImages[idx].height = recal.imageheight; 89 | // temData.images = temImages; 90 | // var bindData = {}; 91 | // bindData[bindName] = temData; 92 | // that.setData(bindData); 93 | var index = temImages[idx].index 94 | var key = `${bindName}` 95 | for (var i of index.split('.')) key+=`.nodes[${i}]` 96 | var keyW = key + '.width' 97 | var keyH = key + '.height' 98 | that.setData({ 99 | [keyW]: recal.imageWidth, 100 | [keyH]: recal.imageheight, 101 | }) 102 | } 103 | 104 | // 计算视觉优先的图片宽高 105 | function wxAutoImageCal(originalWidth, originalHeight,that,bindName) { 106 | //获取图片的原始长宽 107 | var windowWidth = 0, windowHeight = 0; 108 | var autoWidth = 0, autoHeight = 0; 109 | var results = {}; 110 | var padding = that.data[bindName].view.imagePadding; 111 | windowWidth = realWindowWidth-2*padding; 112 | windowHeight = realWindowHeight; 113 | //判断按照那种方式进行缩放 114 | // console.log("windowWidth" + windowWidth); 115 | if (originalWidth > windowWidth) {//在图片width大于手机屏幕width时候 116 | autoWidth = windowWidth; 117 | // console.log("autoWidth" + autoWidth); 118 | autoHeight = (autoWidth * originalHeight) / originalWidth; 119 | // console.log("autoHeight" + autoHeight); 120 | results.imageWidth = autoWidth; 121 | results.imageheight = autoHeight; 122 | } else {//否则展示原来的数据 123 | results.imageWidth = originalWidth; 124 | results.imageheight = originalHeight; 125 | } 126 | return results; 127 | } 128 | 129 | function wxParseTemArray(temArrayName,bindNameReg,total,that){ 130 | var array = []; 131 | var temData = that.data; 132 | var obj = null; 133 | for(var i = 0; i < total; i++){ 134 | var simArr = temData[bindNameReg+i].nodes; 135 | array.push(simArr); 136 | } 137 | 138 | temArrayName = temArrayName || 'wxParseTemArray'; 139 | obj = JSON.parse('{"'+ temArrayName +'":""}'); 140 | obj[temArrayName] = array; 141 | that.setData(obj); 142 | } 143 | 144 | /** 145 | * 配置emojis 146 | * 147 | */ 148 | 149 | function emojisInit(reg='',baseSrc="/wxParse/emojis/",emojis){ 150 | HtmlToJson.emojisInit(reg,baseSrc,emojis); 151 | } 152 | 153 | module.exports = { 154 | wxParse: wxParse, 155 | wxParseTemArray:wxParseTemArray, 156 | emojisInit:emojisInit 157 | } 158 | 159 | 160 | -------------------------------------------------------------------------------- /utils/wxParse/wxParse.wxml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 19 | 20 | 24 | 25 | 35 | 36 | 39 | 40 | 41 |