jyf-parser.vue 25 KB

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