jyf-parser.vue 24 KB

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