jyf-parser.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. <!--
  2. parser 主模块组件
  3. github:https://github.com/jin-yufeng/Parser
  4. docs:https://jin-yufeng.github.io/Parser
  5. 插件市场:https://ext.dcloud.net.cn/plugin?id=805
  6. author:JinYufeng
  7. update:2020/04/14
  8. -->
  9. <template>
  10. <view>
  11. <slot v-if="!nodes.length" />
  12. <!--#ifdef APP-PLUS-NVUE-->
  13. <web-view id="top" ref="web" :src="src" :style="'margin-top:-2px;height:'+height+'px'" @onPostMessage="_message" />
  14. <!--#endif-->
  15. <!--#ifndef APP-PLUS-NVUE-->
  16. <view id="top" :style="showAm+(selectable?';user-select:text;-webkit-user-select:text':'')" :animation="scaleAm" @tap="_tap"
  17. @touchstart="_touchstart" @touchmove="_touchmove">
  18. <!--#ifdef H5-->
  19. <div :id="'rtf'+uid"></div>
  20. <!--#endif-->
  21. <!--#ifndef H5-->
  22. <trees :nodes="nodes" :lazy-load="lazyLoad" :loadVideo="loadVideo" />
  23. <image v-for="(item, index) in imgs" v-bind:key="index" :id="index" :src="item" hidden @load="_load" />
  24. <!--#endif-->
  25. </view>
  26. <!--#endif-->
  27. </view>
  28. </template>
  29. <script>
  30. // +----------------------------------------------------------------------
  31. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  32. // +----------------------------------------------------------------------
  33. // | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
  34. // +----------------------------------------------------------------------
  35. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  36. // +----------------------------------------------------------------------
  37. // | Author: CRMEB Team <admin@crmeb.com>
  38. // +----------------------------------------------------------------------
  39. // #ifndef H5 || APP-PLUS-NVUE
  40. import trees from './libs/trees';
  41. var cache = {},
  42. // #ifdef MP-WEIXIN || MP-TOUTIAO
  43. fs = uni.getFileSystemManager ? uni.getFileSystemManager() : null,
  44. // #endif
  45. Parser = require('./libs/MpHtmlParser.js');
  46. var document; // document 补丁包 https://jin-yufeng.github.io/Parser/#/instructions?id=document
  47. // 计算 cache 的 key
  48. function hash(str) {
  49. for (var i = str.length, val = 5381; i--;)
  50. val += (val << 5) + str.charCodeAt(i);
  51. return val;
  52. }
  53. // #endif
  54. // #ifdef H5 || APP-PLUS-NVUE
  55. var rpx = uni.getSystemInfoSync().screenWidth / 750,
  56. cfg = require('./libs/config.js');
  57. // #endif
  58. // #ifdef APP-PLUS-NVUE
  59. var dom = weex.requireModule('dom');
  60. // #endif
  61. export default {
  62. name: 'parser',
  63. data() {
  64. return {
  65. // #ifdef APP-PLUS
  66. loadVideo: false,
  67. // #endif
  68. // #ifdef H5
  69. uid: this._uid,
  70. // #endif
  71. // #ifdef APP-PLUS-NVUE
  72. src: '',
  73. height: 1,
  74. // #endif
  75. // #ifndef APP-PLUS-NVUE
  76. scaleAm: '',
  77. showAm: '',
  78. imgs: [],
  79. // #endif
  80. nodes: []
  81. }
  82. },
  83. // #ifndef H5 || APP-PLUS-NVUE
  84. components: {
  85. trees
  86. },
  87. // #endif
  88. props: {
  89. 'html': null,
  90. // #ifndef MP-ALIPAY
  91. 'autopause': {
  92. type: Boolean,
  93. default: true
  94. },
  95. // #endif
  96. 'autosetTitle': {
  97. type: Boolean,
  98. default: true
  99. },
  100. // #ifndef H5 || APP-PLUS-NVUE
  101. 'compress': Number,
  102. 'useCache': Boolean,
  103. 'xml': Boolean,
  104. // #endif
  105. 'domain': String,
  106. // #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
  107. 'gestureZoom': Boolean,
  108. // #endif
  109. // #ifdef MP-WEIXIN || MP-QQ || H5 || APP-PLUS
  110. 'lazyLoad': Boolean,
  111. // #endif
  112. 'selectable': Boolean,
  113. 'tagStyle': Object,
  114. 'showWithAnimation': Boolean,
  115. 'useAnchor': Boolean
  116. },
  117. watch: {
  118. html(html) {
  119. this.setContent(html);
  120. }
  121. },
  122. mounted() {
  123. // 图片数组
  124. this.imgList = [];
  125. this.imgList.each = function(f) {
  126. for (var i = 0, len = this.length; i < len; i++)
  127. this.setItem(i, f(this[i], i, this));
  128. }
  129. this.imgList.setItem = function(i, src) {
  130. if (i == void 0 || !src) return;
  131. // #ifndef MP-ALIPAY || APP-PLUS
  132. // 去重
  133. if (src.indexOf('http') == 0 && this.includes(src)) {
  134. var newSrc = '';
  135. for (var j = 0, c; c = src[j]; j++) {
  136. if (c == '/' && src[j - 1] != '/' && src[j + 1] != '/') break;
  137. newSrc += Math.random() > 0.5 ? c.toUpperCase() : c;
  138. }
  139. newSrc += src.substr(j);
  140. return this[i] = newSrc;
  141. }
  142. // #endif
  143. this[i] = src;
  144. // 暂存 data src
  145. if (src.includes('data:image')) {
  146. var filePath, info = src.match(/data:image\/(\S+?);(\S+?),(.+)/);
  147. if (!info) return;
  148. // #ifdef MP-WEIXIN || MP-TOUTIAO
  149. filePath = `${wx.env.USER_DATA_PATH}/${Date.now()}.${info[1]}`;
  150. fs && fs.writeFile({
  151. filePath,
  152. data: info[3],
  153. encoding: info[2],
  154. success: () => this[i] = filePath
  155. })
  156. // #endif
  157. // #ifdef APP-PLUS
  158. filePath = `_doc/parser_tmp/${Date.now()}.${info[1]}`;
  159. var bitmap = new plus.nativeObj.Bitmap();
  160. bitmap.loadBase64Data(src, () => {
  161. bitmap.save(filePath, {}, () => {
  162. bitmap.clear()
  163. this[i] = filePath;
  164. })
  165. })
  166. // #endif
  167. }
  168. }
  169. if (this.html) this.setContent(this.html);
  170. },
  171. beforeDestroy() {
  172. // #ifdef H5
  173. if (this._observer) this._observer.disconnect();
  174. // #endif
  175. this.imgList.each(src => {
  176. // #ifdef APP-PLUS
  177. if (src && src.includes('_doc')) {
  178. plus.io.resolveLocalFileSystemURL(src, entry => {
  179. entry.remove();
  180. });
  181. }
  182. // #endif
  183. // #ifdef MP-WEIXIN || MP-TOUTIAO
  184. if (src && src.includes(uni.env.USER_DATA_PATH))
  185. fs && fs.unlink({
  186. filePath: src
  187. })
  188. // #endif
  189. })
  190. clearInterval(this._timer);
  191. },
  192. methods: {
  193. // #ifdef H5 || APP-PLUS-NVUE
  194. _Dom2Str(nodes) {
  195. var str = '';
  196. for (var node of nodes) {
  197. if (node.type == 'text')
  198. str += node.text;
  199. else {
  200. str += ('<' + node.name);
  201. for (var attr in node.attrs || {})
  202. str += (' ' + attr + '="' + node.attrs[attr] + '"');
  203. if (!node.children || !node.children.length) str += '>';
  204. else str += ('>' + this._Dom2Str(node.children) + '</' + node.name + '>');
  205. }
  206. }
  207. return str;
  208. },
  209. _handleHtml(html, append) {
  210. if (typeof html != 'string') html = this._Dom2Str(html.nodes || html);
  211. // 处理 rpx
  212. if (html.includes('rpx'))
  213. html = html.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * rpx + 'px');
  214. if (!append) {
  215. // 处理 tag-style 和 userAgentStyles
  216. var style = '<style>@keyframes show{0%{opacity:0}100%{opacity:1}}';
  217. for (var item in cfg.userAgentStyles)
  218. style += `${item}{${cfg.userAgentStyles[item]}}`;
  219. for (item in this.tagStyle)
  220. style += `${item}{${this.tagStyle[item]}}`;
  221. style += '</style>';
  222. html = style + html;
  223. }
  224. return html;
  225. },
  226. // #endif
  227. setContent(html, append) {
  228. // #ifdef APP-PLUS-NVUE
  229. if (!html) {
  230. this.src = '';
  231. this.height = 1;
  232. return;
  233. }
  234. if (append) return;
  235. plus.io.resolveLocalFileSystemURL('_doc', entry => {
  236. entry.getDirectory('parser_tmp', {
  237. create: true
  238. }, entry => {
  239. var fileName = Date.now() + '.html';
  240. entry.getFile(fileName, {
  241. create: true
  242. }, entry => {
  243. entry.createWriter(writer => {
  244. writer.onwriteend = () => {
  245. this.nodes = [1];
  246. this.src = '_doc/parser_tmp/' + fileName;
  247. this.$nextTick(function() {
  248. entry.remove();
  249. })
  250. }
  251. html =
  252. '<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1' +
  253. (this.selectable ? '' : ',user-scalable=no') +
  254. '"><script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></' +
  255. 'script><base href="' + this.domain + '">' + this._handleHtml(html) +
  256. '<script>"use strict";function post(t){uni.postMessage({data:t})}' +
  257. (this.showWithAnimation ? 'document.body.style.animation="show .5s",' : '') +
  258. 'document.addEventListener("UniAppJSBridgeReady",function(){post({action:"load",text:document.body.innerText});var t=document.getElementsByTagName("title");t.length&&post({action:"getTitle",title:t[0].innerText});for(var e,o=document.getElementsByTagName("img"),n=[],i=0,r=0;e=o[i];i++)e.onerror=function(){post({action:"error",source:"img",target:this})},e.hasAttribute("ignore")||"A"==e.parentElement.nodeName||(e.i=r++,n.push(e.src),e.onclick=function(){post({action:"preview",img:{i:this.i,src:this.src}})});post({action:"getImgList",imgList:n});for(var a,s=document.getElementsByTagName("a"),c=0;a=s[c];c++)a.onclick=function(){var t,e=this.getAttribute("href");if("#"==e[0]){var r=document.getElementById(e.substr(1));r&&(t=r.offsetTop)}return post({action:"linkpress",href:e,offset:t}),!1};;for(var u,m=document.getElementsByTagName("video"),d=0;u=m[d];d++)u.style.maxWidth="100%;",u.onerror=function(){post({action:"error",source:"video",target:this})}' +
  259. (this.autopause ? ',u.onplay=function(){for(var t,e=0;t=m[e];e++)t!=this&&t.pause()}' : '') +
  260. ';for(var g,l=document.getElementsByTagName("audio"),p=0;g=l[p];p++)g.onerror=function(){post({action:"error",source:"audio",target:this})};window.onload=function(){post({action:"ready",height:document.body.scrollHeight})}});</' +
  261. 'script>';
  262. writer.write(html);
  263. });
  264. })
  265. })
  266. })
  267. // #endif
  268. // #ifdef H5
  269. if (!html) {
  270. if (this.rtf && !append) this.rtf.parentNode.removeChild(this.rtf);
  271. return;
  272. }
  273. var div = document.createElement('div');
  274. if (!append) {
  275. if (this.rtf) this.rtf.parentNode.removeChild(this.rtf);
  276. this.rtf = div;
  277. } else {
  278. if (!this.rtf) this.rtf = div;
  279. else this.rtf.appendChild(div);
  280. }
  281. div.innerHTML = this._handleHtml(html, append);
  282. for (var styles = this.rtf.getElementsByTagName('style'), i = 0, style; style = styles[i++];) {
  283. style.innerHTML = style.innerHTML.replace(/body/g, '#rtf' + this._uid);
  284. style.setAttribute('scoped', 'true');
  285. }
  286. // 懒加载
  287. if (!this._observer && this.lazyLoad && IntersectionObserver) {
  288. this._observer = new IntersectionObserver(changes => {
  289. for (let item, i = 0; item = changes[i++];) {
  290. if (item.isIntersecting) {
  291. item.target.src = item.target.getAttribute('data-src');
  292. item.target.removeAttribute('data-src');
  293. this._observer.unobserve(item.target);
  294. }
  295. }
  296. }, {
  297. rootMargin: '900px 0px 900px 0px'
  298. })
  299. }
  300. var _ts = this;
  301. // 获取标题
  302. var title = this.rtf.getElementsByTagName('title');
  303. if (title.length && this.autosetTitle)
  304. uni.setNavigationBarTitle({
  305. title: title[0].innerText
  306. })
  307. // 图片处理
  308. this.imgList.length = 0;
  309. var imgs = this.rtf.getElementsByTagName('img');
  310. for (let i = 0, j = 0, img; img = imgs[i]; i++) {
  311. img.style.maxWidth = '100%';
  312. var src = img.getAttribute('src');
  313. if (this.domain && src) {
  314. if (src[0] == '/') {
  315. if (src[1] == '/')
  316. img.src = (this.domain.includes('://') ? this.domain.split('://')[0] : '') + ':' + src;
  317. else img.src = this.domain + src;
  318. } else if (!src.includes('://')) img.src = this.domain + '/' + src;
  319. }
  320. if (!img.hasAttribute('ignore') && img.parentElement.nodeName != 'A') {
  321. img.i = j++;
  322. _ts.imgList.push(img.src || img.getAttribute('data-src'));
  323. img.onclick = function() {
  324. var preview = true;
  325. this.ignore = () => preview = false;
  326. _ts.$emit('imgtap', this);
  327. if (preview) {
  328. uni.previewImage({
  329. current: this.i,
  330. urls: _ts.imgList
  331. });
  332. }
  333. }
  334. }
  335. img.onerror = function() {
  336. _ts.$emit('error', {
  337. source: 'img',
  338. target: this
  339. });
  340. }
  341. if (_ts.lazyLoad && this._observer && img.src && img.i != 0) {
  342. img.setAttribute('data-src', img.src);
  343. img.removeAttribute('src');
  344. this._observer.observe(img);
  345. }
  346. }
  347. // 链接处理
  348. var links = this.rtf.getElementsByTagName('a');
  349. for (var link of links) {
  350. link.onclick = function() {
  351. var jump = true,
  352. href = this.getAttribute('href');
  353. _ts.$emit('linkpress', {
  354. href,
  355. ignore: () => jump = false
  356. });
  357. if (jump && href) {
  358. if (href[0] == '#') {
  359. if (_ts.useAnchor) {
  360. _ts.navigateTo({
  361. id: href.substr(1)
  362. })
  363. }
  364. } else if (href.indexOf('http') == 0 || href.indexOf('//') == 0)
  365. return true;
  366. else {
  367. uni.navigateTo({
  368. url: href
  369. })
  370. }
  371. }
  372. return false;
  373. }
  374. }
  375. // 视频处理
  376. var videos = this.rtf.getElementsByTagName('video');
  377. _ts.videoContexts = videos;
  378. for (let video, i = 0; video = videos[i++];) {
  379. video.style.maxWidth = '100%';
  380. video.style.width = '100%';
  381. video.onerror = function() {
  382. _ts.$emit('error', {
  383. source: 'video',
  384. target: this
  385. });
  386. }
  387. video.onplay = function() {
  388. if (_ts.autopause)
  389. for (let item, i = 0; item = _ts.videoContexts[i++];)
  390. if (item != this) item.pause();
  391. }
  392. }
  393. // 音频处理
  394. var audios = this.rtf.getElementsByTagName('audios');
  395. for (var audio of audios)
  396. audio.onerror = function() {
  397. _ts.$emit('error', {
  398. source: 'audio',
  399. target: this
  400. });
  401. }
  402. this.document = this.rtf;
  403. if (!append) document.getElementById('rtf' + this._uid).appendChild(this.rtf);
  404. this.$nextTick(() => {
  405. this.nodes = [1];
  406. this.$emit('load');
  407. })
  408. setTimeout(() => this.showAm = '', 500);
  409. // #endif
  410. // #ifndef H5 || APP-PLUS-NVUE
  411. var nodes;
  412. if (!html)
  413. return this.nodes = [];
  414. else if (typeof html == 'string') {
  415. let parser = new Parser(html, this);
  416. // 缓存读取
  417. if (this.useCache) {
  418. var hashVal = hash(html);
  419. if (cache[hashVal])
  420. nodes = cache[hashVal];
  421. else {
  422. nodes = parser.parse();
  423. cache[hashVal] = nodes;
  424. }
  425. } else nodes = parser.parse();
  426. this.$emit('parse', nodes);
  427. } else if (Object.prototype.toString.call(html) == '[object Array]') {
  428. // 非本插件产生的 array 需要进行一些转换
  429. if (html.length && html[0].PoweredBy != 'Parser') {
  430. let parser = new Parser(html, this);
  431. (function f(ns) {
  432. for (var i = 0, n; n = ns[i]; i++) {
  433. if (n.type == 'text') continue;
  434. n.attrs = n.attrs || {};
  435. for (var item in n.attrs)
  436. if (typeof n.attrs[item] != 'string') n.attrs[item] = n.attrs[item].toString();
  437. parser.matchAttr(n, parser);
  438. if (n.children && n.children.length) {
  439. parser.STACK.push(n);
  440. f(n.children);
  441. parser.popNode(parser.STACK.pop());
  442. } else n.children = void 0;
  443. }
  444. })(html);
  445. }
  446. nodes = html;
  447. } else if (typeof html == 'object' && html.nodes) {
  448. nodes = html.nodes;
  449. console.warn('错误的 html 类型:object 类型已废弃');
  450. } else
  451. return console.warn('错误的 html 类型:' + typeof html);
  452. // #ifdef APP-PLUS
  453. this.loadVideo = false;
  454. // #endif
  455. if (document) this.document = new document(this.nodes, 'nodes', this);
  456. if (append) this.nodes = this.nodes.concat(nodes);
  457. else this.nodes = nodes;
  458. if (nodes.length && nodes[0].title && this.autosetTitle)
  459. uni.setNavigationBarTitle({
  460. title: nodes[0].title
  461. })
  462. this.$nextTick(() => {
  463. this.imgList.length = 0;
  464. this.videoContexts = [];
  465. // #ifdef MP-TOUTIAO
  466. setTimeout(() => {
  467. // #endif
  468. var f = (cs) => {
  469. for (let i = 0, c; c = cs[i++];) {
  470. if (c.$options.name == 'trees') {
  471. for (var j = c.nodes.length, item; item = c.nodes[--j];) {
  472. if (item.c) continue;
  473. if (item.name == 'img') {
  474. this.imgList.setItem(item.attrs.i, item.attrs.src);
  475. // #ifndef MP-ALIPAY
  476. if (!c.observer && !c.imgLoad && item.attrs.i != '0') {
  477. if (this.lazyLoad && uni.createIntersectionObserver) {
  478. c.observer = uni.createIntersectionObserver(c);
  479. c.observer.relativeToViewport({
  480. top: 900,
  481. bottom: 900
  482. }).observe('._img', () => {
  483. c.imgLoad = true;
  484. c.observer.disconnect();
  485. })
  486. } else
  487. c.imgLoad = true;
  488. }
  489. // #endif
  490. }
  491. // #ifndef MP-ALIPAY
  492. else if (item.name == 'video') {
  493. var ctx = uni.createVideoContext(item.attrs.id, c);
  494. ctx.id = item.attrs.id;
  495. this.videoContexts.push(ctx);
  496. }
  497. // #endif
  498. // #ifdef MP-BAIDU || MP-ALIPAY || APP-PLUS
  499. if (item.attrs && item.attrs.id) {
  500. this.anchors = this.anchors || [];
  501. this.anchors.push({
  502. id: item.attrs.id,
  503. node: c
  504. })
  505. }
  506. // #endif
  507. }
  508. }
  509. if (c.$children.length)
  510. f(c.$children)
  511. }
  512. }
  513. f(this.$children);
  514. // #ifdef MP-TOUTIAO
  515. }, 200)
  516. this.$emit('load');
  517. // #endif
  518. // #ifdef APP-PLUS
  519. setTimeout(() => {
  520. this.loadVideo = true;
  521. }, 3000);
  522. // #endif
  523. })
  524. // #endif
  525. // #ifndef APP-PLUS-NVUE
  526. var height;
  527. clearInterval(this._timer);
  528. this._timer = setInterval(() => {
  529. // #ifdef H5
  530. var res = [this.rtf.getBoundingClientRect()];
  531. // #endif
  532. // #ifndef H5
  533. // #ifdef APP-PLUS
  534. uni.createSelectorQuery().in(this)
  535. // #endif
  536. // #ifndef APP-PLUS
  537. this.createSelectorQuery()
  538. // #endif
  539. .select('#top').boundingClientRect().exec(res => {
  540. // #endif
  541. this.width = res[0].width;
  542. if (res[0].height == height) {
  543. this.$emit('ready', res[0])
  544. clearInterval(this._timer);
  545. }
  546. height = res[0].height;
  547. // #ifndef H5
  548. });
  549. // #endif
  550. }, 350)
  551. if (this.showWithAnimation && !append) this.showAm = 'animation:show .5s';
  552. // #endif
  553. },
  554. getText(ns = this.nodes) {
  555. // #ifdef APP-PLUS-NVUE
  556. return this._text;
  557. // #endif
  558. // #ifdef H5
  559. return this.rtf.innerText;
  560. // #endif
  561. // #ifndef H5 || APP-PLUS-NVUE
  562. var txt = '';
  563. for (var i = 0, n; n = ns[i++];) {
  564. if (n.type == 'text') txt += n.text.replace(/&nbsp;/g, '\u00A0').replace(/&lt;/g, '<').replace(/&gt;/g, '>')
  565. .replace(/&amp;/g, '&');
  566. else if (n.type == 'br') txt += '\n';
  567. else {
  568. // 块级标签前后加换行
  569. var block = n.name == 'p' || n.name == 'div' || n.name == 'tr' || n.name == 'li' || (n.name[0] == 'h' && n.name[1] >
  570. '0' && n.name[1] < '7');
  571. if (block && txt && txt[txt.length - 1] != '\n') txt += '\n';
  572. if (n.children) txt += this.getText(n.children);
  573. if (block && txt[txt.length - 1] != '\n') txt += '\n';
  574. else if (n.name == 'td' || n.name == 'th') txt += '\t';
  575. }
  576. }
  577. return txt;
  578. // #endif
  579. },
  580. navigateTo(obj) {
  581. if (!this.useAnchor)
  582. return obj.fail && obj.fail({
  583. errMsg: 'Anchor is disabled'
  584. })
  585. // #ifdef APP-PLUS-NVUE
  586. if (!obj.id)
  587. dom.scrollToElement(this.$refs.web);
  588. else
  589. this.$refs.web.evalJs('var pos=document.getElementById("' + obj.id +
  590. '");if(pos)post({action:"linkpress",href:"#",offset:pos.offsetTop})');
  591. return obj.success && obj.success({
  592. errMsg: 'pageScrollTo:ok'
  593. });
  594. // #endif
  595. // #ifdef H5
  596. if (!obj.id) {
  597. window.scrollTo(0, this.rtf.offsetTop);
  598. return obj.success && obj.success({
  599. errMsg: 'pageScrollTo:ok'
  600. });
  601. }
  602. var target = document.getElementById(obj.id);
  603. if (!target) return obj.fail && obj.fail({
  604. errMsg: 'Label not found'
  605. });
  606. obj.scrollTop = this.rtf.offsetTop + target.offsetTop;
  607. uni.pageScrollTo(obj);
  608. // #endif
  609. // #ifndef H5
  610. var Scroll = (selector, component) => {
  611. uni.createSelectorQuery().in(component ? component : this).select(selector).boundingClientRect().selectViewport()
  612. .scrollOffset()
  613. .exec(res => {
  614. if (!res || !res[0])
  615. return obj.fail && obj.fail({
  616. errMsg: 'Label not found'
  617. });
  618. obj.scrollTop = res[1].scrollTop + res[0].top;
  619. uni.pageScrollTo(obj);
  620. })
  621. }
  622. if (!obj.id) Scroll('#top');
  623. else {
  624. // #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
  625. Scroll('#top >>> #' + obj.id + ', #top >>> .' + obj.id);
  626. // #endif
  627. // #ifdef MP-BAIDU || MP-ALIPAY || APP-PLUS
  628. for (var anchor of this.anchors)
  629. if (anchor.id == obj.id)
  630. Scroll('#' + obj.id + ', .' + obj.id, anchor.node);
  631. // #endif
  632. }
  633. // #endif
  634. },
  635. getVideoContext(id) {
  636. // #ifndef APP-PLUS-NVUE
  637. if (!id) return this.videoContexts;
  638. else
  639. for (var i = this.videoContexts.length; i--;)
  640. if (this.videoContexts[i].id == id) return this.videoContexts[i];
  641. // #endif
  642. },
  643. // 预加载
  644. preLoad(html, num) {
  645. // #ifdef H5 || APP-PLUS-NVUE
  646. if (html.constructor == Array)
  647. html = this._Dom2Str(html);
  648. var script = "var contain=document.createElement('div');contain.innerHTML='" + html.replace(/'/g, "\\'") +
  649. "';for(var imgs=contain.querySelectorAll('img'),i=imgs.length-1;i>=" + num +
  650. ";i--)imgs[i].removeAttribute('src');";
  651. // #endif
  652. // #ifdef APP-PLUS-NVUE
  653. this.$refs.web.evalJs(script);
  654. // #endif
  655. // #ifdef H5
  656. eval(script);
  657. // #endif
  658. // #ifndef H5 || APP-PLUS-NVUE
  659. if (typeof html == 'string') {
  660. var id = hash(html);
  661. html = new Parser(html, this).parse();
  662. cache[id] = html;
  663. }
  664. var wait = [];
  665. (function f(ns) {
  666. for (var i = 0, n; n = ns[i++];) {
  667. if (n.name == 'img' && n.attrs.src && !wait.includes(n.attrs.src))
  668. wait.push(n.attrs.src);
  669. f(n.children || []);
  670. }
  671. })(html);
  672. if (num) wait = wait.slice(0, num);
  673. this._wait = (this._wait || []).concat(wait);
  674. if (!this.imgs) this.imgs = this._wait.splice(0, 15);
  675. else if (this.imgs.length < 15)
  676. this.imgs = this.imgs.concat(this._wait.splice(0, 15 - this.imgs.length));
  677. // #endif
  678. },
  679. // #ifdef APP-PLUS-NVUE
  680. _message(e) {
  681. // 接收 web-view 消息
  682. var data = e.detail.data[0];
  683. if (data.action == 'load') {
  684. this.$emit('load');
  685. this._text = data.text;
  686. } else if (data.action == 'getTitle') {
  687. if (this.autosetTitle)
  688. uni.setNavigationBarTitle({
  689. title: data.title
  690. })
  691. } else if (data.action == 'getImgList') {
  692. this.imgList.length = 0;
  693. for (var i = data.imgList.length; i--;)
  694. this.imgList.setItem(i, data.imgList[i]);
  695. } else if (data.action == 'preview') {
  696. var preview = true;
  697. data.img.ignore = () => preview = false;
  698. this.$emit('imgtap', data.img);
  699. if (preview)
  700. uni.previewImage({
  701. current: data.img.i,
  702. urls: this.imgList
  703. })
  704. } else if (data.action == 'linkpress') {
  705. var jump = true,
  706. href = data.href;
  707. this.$emit('linkpress', {
  708. href,
  709. ignore: () => jump = false
  710. })
  711. if (jump && href) {
  712. if (href[0] == '#') {
  713. if (this.useAnchor)
  714. dom.scrollToElement(this.$refs.web, {
  715. offset: data.offset
  716. })
  717. } else if (href.includes('://'))
  718. plus.runtime.openWeb(href);
  719. else
  720. uni.navigateTo({
  721. url: href
  722. })
  723. }
  724. } else if (data.action == 'error')
  725. this.$emit('error', {
  726. source: data.source,
  727. target: data.target
  728. })
  729. else if (data.action == 'ready') {
  730. this.height = data.height;
  731. this.$nextTick(() => {
  732. uni.createSelectorQuery().in(this).select('#top').boundingClientRect().exec(res => {
  733. this.rect = res[0];
  734. this.$emit('ready', res[0]);
  735. })
  736. })
  737. }
  738. },
  739. // #endif
  740. // #ifndef APP-PLUS-NVUE
  741. // #ifndef H5
  742. _load(e) {
  743. if (this._wait.length)
  744. this.$set(this.imgs, e.target.id, this._wait.shift());
  745. },
  746. // #endif
  747. _tap(e) {
  748. // #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
  749. if (this.gestureZoom && e.timeStamp - this._lastT < 300) {
  750. var initY = e.touches[0].pageY - e.currentTarget.offsetTop;
  751. if (this._zoom) {
  752. this._scaleAm.translateX(0).scale(1).step();
  753. uni.pageScrollTo({
  754. scrollTop: (initY + this._initY) / 2 - e.touches[0].clientY,
  755. duration: 400
  756. })
  757. } else {
  758. var initX = e.touches[0].pageX - e.currentTarget.offsetLeft;
  759. this._initY = initY;
  760. this._scaleAm = uni.createAnimation({
  761. transformOrigin: `${initX}px ${this._initY}px 0`,
  762. timingFunction: 'ease-in-out'
  763. });
  764. // #ifdef MP-TOUTIAO
  765. this._scaleAm.opacity(1);
  766. // #endif
  767. this._scaleAm.scale(2).step();
  768. this._tMax = initX / 2;
  769. this._tMin = (initX - this.width) / 2;
  770. this._tX = 0;
  771. }
  772. this._zoom = !this._zoom;
  773. this.scaleAm = this._scaleAm.export();
  774. }
  775. this._lastT = e.timeStamp;
  776. // #endif
  777. },
  778. _touchstart(e) {
  779. // #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
  780. if (e.touches.length == 1)
  781. this._initX = this._lastX = e.touches[0].pageX;
  782. // #endif
  783. },
  784. _touchmove(e) {
  785. // #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
  786. var diff = e.touches[0].pageX - this._lastX;
  787. if (this._zoom && e.touches.length == 1 && Math.abs(diff) > 20) {
  788. this._lastX = e.touches[0].pageX;
  789. if ((this._tX <= this._tMin && diff < 0) || (this._tX >= this._tMax && diff > 0))
  790. return;
  791. this._tX += (diff * Math.abs(this._lastX - this._initX) * 0.05);
  792. if (this._tX < this._tMin) this._tX = this._tMin;
  793. if (this._tX > this._tMax) this._tX = this._tMax;
  794. this._scaleAm.translateX(this._tX).step();
  795. this.scaleAm = this._scaleAm.export();
  796. }
  797. // #endif
  798. }
  799. // #endif
  800. }
  801. }
  802. </script>
  803. <style>
  804. @keyframes show {
  805. 0% {
  806. opacity: 0
  807. }
  808. 100% {
  809. opacity: 1;
  810. }
  811. }
  812. /* #ifdef MP-WEIXIN */
  813. :host {
  814. display: block;
  815. overflow: scroll;
  816. -webkit-overflow-scrolling: touch;
  817. }
  818. /* #endif */
  819. </style>