diff --git a/src/js/image_preview.js b/src/js/image_preview.js
deleted file mode 100644
index 60c2d23f..00000000
--- a/src/js/image_preview.js
+++ /dev/null
@@ -1,186 +0,0 @@
-'use strict';
-
-import { ImageURL } from './image_urls';
-
-// ImagePreview shows a spinner or a image that moves along with cursor.
-// It shows a spinner and hides image when the image is loading. Reverse
-// afterwards.
-//
-// @prop src the URL of image to diaplay; empty string hides components.
-class ImagePreview extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- left: 0,
- top: 0,
- spinning: !!this.props.src,
- };
- }
-
- componentDidMount() {
- this._mouseMoveListener = this._onMouseMove.bind(this);
- document.addEventListener('mousemove', this._mouseMoveListener);
- }
-
- componentWillUnmount() {
- if (this._mouseMoveListener) {
- document.removeEventListener('mousemove', this._mouseMoveListener);
- }
- }
-
- componentWillReceiveProps(nextProps) {
- if (this.props.src != nextProps.src) {
- this.setState({spinning: !!nextProps.src});
- }
- }
-
- _onMouseMove(e) {
- this.setState({
- left: e.clientX,
- top: e.clientY
- });
- }
-
- _imageStyle() {
- if (!this._preview || this.props.src === '' || this.state.spinning) {
- return {'display': 'none'};
- }
-
- let mouseHeight = this.state.top;
- let curX = this.state.left;
- let pageHeight = $(window).height();
- let imageHeight = this._preview.clientHeight;
- let imgTop = 20;
-
- // opening image would pass the bottom of the page
- if (mouseHeight + imageHeight / 2 > pageHeight - 20) {
- if (imageHeight / 2 < mouseHeight) {
- imgTop = pageHeight - 20 - imageHeight;
- }
- } else if (mouseHeight - 20 > imageHeight / 2) {
- imgTop = mouseHeight - imageHeight / 2;
- }
-
- return {
- 'display': 'block',
- 'position': 'absolute',
- 'left': (curX + 20) + 'px',
- 'top': imgTop + 'px',
- 'maxHeight': '80%',
- 'maxWidth': '90%',
- 'zIndex': 2
- };
- }
-
- _spinnerStyle() {
- if (!this.state.spinning) {
- return {'display': 'none'};
- }
- return {
- 'position': 'absolute',
- 'left': (this.state.left + 20) + 'px',
- 'top': this.state.top + 'px',
- 'zIndex': 2
- };
- }
-
- _stopSpinner() {
- this.setState({spinning: false});
- }
-
- _onImageLoaded() {
- this._stopSpinner();
- // Recalculate position.
- this.forceUpdate();
- }
-
- render() {
- return (
-
-
{ this._preview = img; }}
- src={this.props.src}
- style={this._imageStyle()}
- onLoad={() => this._onImageLoaded()}>
-
-
- );
- }
-}
-
-// BaseAsyncImagePreview is a base class for general async image.
-// When the image URL is determined, the loadable image URL is available via
-// resolvedSrc in state.
-//
-// @prop src the URL for image; it accepts indirect links by using ImageURL
-// module.
-//
-// TODO: This should be a plain class rather than extending from
-// React.Component.
-class BaseAsyncImagePreview extends React.Component {
- constructor(props) {
- super(props);
- this.state = {resolvedSrc: ''};
- }
-
- componentDidMount() {
- this._startFetch(this.props.src);
- }
-
- componentWillReceiveProps(nextProps) {
- if (this.props.src != nextProps.src) {
- this._startFetch(nextProps.src);
- }
- }
-
- _startFetch(src) {
- // Remove previous image.
- this.setState({resolvedSrc: ''});
- if (!src) {
- return;
- }
- let p = ImageURL.create(src);
- if (p) {
- let cb = (resolvedSrc) => {
- this._onResolve(resolvedSrc, src);
- };
- p.fetchSrc().then(cb);
- }
- }
-
- _onResolve(src, forSrc) {
- if (forSrc != this.props.src) {
- // Outdated result.
- return;
- }
- this.setState({resolvedSrc: src});
- }
-}
-
-// AsyncImagePreview resolves image location using ImageURL and displays as
-// ImagePreview.
-class AsyncImagePreview extends BaseAsyncImagePreview {
- render() {
- return
;
- }
-}
-
-export function renderImagePreview(cont, src) {
- return ReactDOM.render(
, cont);
-}
-
-// HyperLinkPreview resolves image location using ImageURL and displays with
-// a img element.
-export class HyperLinkPreview extends BaseAsyncImagePreview {
- render() {
- if (this.state.resolvedSrc) {
- return
;
- } else {
- return
;
- }
- }
-}
diff --git a/src/js/image_urls.js b/src/js/image_urls.js
deleted file mode 100644
index b5aa7bc4..00000000
--- a/src/js/image_urls.js
+++ /dev/null
@@ -1,106 +0,0 @@
-'use struct';
-
-export class ImageURL {
- static register(clazz) {
- if (!this._providers) {
- this._providers = [];
- }
- this._providers.push(clazz);
- }
-
- static create(href) {
- for (let provider of this._providers) {
- let instance = provider.create(href);
- if (instance) {
- return instance;
- }
- }
- return null;
- }
-}
-
-class FlickrImageURL {
- // To decode base58 of flickr photo id
- // ref: https://www.flickr.com/groups/51035612836@N01/discuss/72157616713786392/72157620931323757
- static base58_decode(snipcode) {
- var alphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
- var num = snipcode.length;
- var decoded = 0;
- var multi = 1;
- for (var i = (num-1); i >= 0; i--) {
- decoded = decoded + multi * alphabet.indexOf(snipcode[i]);
- multi = multi * alphabet.length;
- }
- return decoded;
- }
-
- static create(href) {
- let found_flickr = href.match(
- 'flic\.kr\/p\/\(\\w\+\)|flickr\.com\/photos\/[\\w@]\+\/\(\\d\+\)');
- if (!found_flickr) {
- return null;
- }
- let flickrBase58Id = found_flickr[1];
- let flickrPhotoId = flickrBase58Id ?
- this.base58_decode(flickrBase58Id) : found_flickr[2];
- return new FlickrImageURL(flickrPhotoId);
- }
-
- constructor(photoID) {
- this._photoID = photoID;
- }
-
- fetchSrc() {
- const apiURL = "https://api.flickr.com/services/rest/" +
- "?method=flickr.photos.getInfo" +
- "&api_key=c8c95356e465b8d7398ff2847152740e" +
- "&photo_id=" + encodeURIComponent(this._photoID) +
- "&format=json" +
- "&jsoncallback=?";
- return new Promise(function(resolve, reject) {
- $.getJSON(apiURL, function(data) {
- if (data.photo) {
- let p = data.photo;
- let src = "https://farm" + p.farm + ".staticflickr.com/"+
- p.server + "/" + p.id + "_" + p.secret + ".jpg";
- resolve(src);
- } else {
- reject();
- }
- });
- });
- }
-}
-ImageURL.register(FlickrImageURL);
-
-class ImgurImageURL {
- static create(href) {
- if (href.match('^https?://(i\.)?imgur\.com/')) {
- return new ImgurImageURL(href);
- }
- return null;
- }
-
- constructor(href) {
- this._href = href;
- }
-
- fetchSrc() {
- let src = this._href.replace(
- /^https?:\/\/imgur.com/, 'https://i.imgur.com') + '.jpg';
- return Promise.resolve(src);
- }
-}
-ImageURL.register(ImgurImageURL);
-
-class GenericImageURL {
- static create(href) {
- // TODO: add support for common suffixes.
- return null;
- }
-
- fetchSrc() {
- return Promise.reject('not implemented');
- }
-}
-ImageURL.register(GenericImageURL);
diff --git a/src/js/pttchrome.js b/src/js/pttchrome.js
index 4aadcca4..cf3465c9 100644
--- a/src/js/pttchrome.js
+++ b/src/js/pttchrome.js
@@ -724,7 +724,7 @@ App.prototype.getWindowInnerBounds = function() {
};
App.prototype.getFirstGridOffsets = function() {
- var container = $("#mainContainer")[0];
+ var container = $(".main")[0];
return {
top: container.offsetTop,
left: container.offsetLeft
diff --git a/src/js/term_ui.js b/src/js/term_ui.js
index 1bdc12b1..61f128da 100644
--- a/src/js/term_ui.js
+++ b/src/js/term_ui.js
@@ -1,4 +1,5 @@
-import Row from '../components/Row';
+import Row from "../components/Row";
+import Screen from "../components/Screen";
export class ColorState {
constructor(fg, bg, blink) {
@@ -15,8 +16,27 @@ export class ColorState {
}
}
-export function renderRowHtml(chars, row, forceWidth, showsLinkPreviews, cont) {
+/**
+ * @deprecated
+ */
+export function renderRowHtml(chars, row, forceWidth, enableLinkInlinePreview, cont) {
return ReactDOM.render(
-
|
, cont);
+
|
,
+ cont
+ );
+}
+
+export function renderScreen(lines, forceWidth, enableLinkInlinePreview, enableLinkHoverPreview, cont) {
+ return ReactDOM.render(
+
, cont);
}
diff --git a/src/js/term_view.js b/src/js/term_view.js
index 266270ef..e0de8720 100644
--- a/src/js/term_view.js
+++ b/src/js/term_view.js
@@ -2,8 +2,7 @@
import { TermKeyboard } from './term_keyboard';
import { termInvColors } from './term_buf';
-import { renderImagePreview } from './image_preview';
-import { renderRowHtml } from './term_ui';
+import { renderRowHtml, renderScreen } from './term_ui';
import { i18n } from './i18n';
import { setTimer } from './util';
import { wrapText, u2b, parseStatusRow } from './string_util';
@@ -24,9 +23,6 @@ export function TermView(rowCount) {
this.charset = 'big5';
this.EnterChar = '\r';
this.EscChar = '\x15'; // Ctrl-U
- this.dropToPaste = false;
- this.ctrlPicturePreview = false;
- this.picturePreviewInfo = false;
this.middleButtonFunction = 0;
this.leftButtonFunction = false;
this.mouseWheelFunction1 = 1;
@@ -38,7 +34,6 @@ export function TermView(rowCount) {
this.fontFitWindowWidth = false;
this.verticalAlignCenter = true;
this.horizontalAlignCenter = true;
- this.easyReadingWithImg = false;
//new pref - end
this.bbsViewMargin = 0;
@@ -58,7 +53,6 @@ export function TermView(rowCount) {
this.doHighlightOnCurRow = false;
- this.displayingRows = [];
this.curRow = 0;
this.curCol = 0;
@@ -73,6 +67,11 @@ export function TermView(rowCount) {
this.doBlink = true;
this.cursorBlinkTimer = null;
+ // React
+ this.componentScreen = {
+ setCurrentHighlighted() {},
+ };
+
this.selection = null;
this.input = document.getElementById('t');
this.bbsCursor = document.getElementById('cursor');
@@ -95,16 +94,14 @@ export function TermView(rowCount) {
this.titleTimer = null;
this.notif = null;
- var mainDiv = document.createElement('div');
- mainDiv.setAttribute('class', 'main');
- var defaultRows = '';
- for (var i = 0; i < rowCount; ++i) {
- defaultRows += '
';
- this.displayingRows.push(null);
- }
- mainDiv.innerHTML = '
' + defaultRows + '
';
- this.BBSWin.appendChild(mainDiv);
- this.mainDisplay = mainDiv;
+ Object.defineProperty(this, 'mainContainer', {
+ get: function() { return $('#mainContainer')[0] },
+ });
+
+ var mainDisplay = document.createElement('div');
+ mainDisplay.setAttribute('class', 'main');
+ this.BBSWin.appendChild(mainDisplay);
+ this.mainDisplay = mainDisplay;
var lastRowDiv = document.createElement('div');
lastRowDiv.setAttribute('id', 'easyReadingLastRow');
@@ -120,7 +117,6 @@ export function TermView(rowCount) {
this.replyRowDiv = replyRowDiv;
this.BBSWin.appendChild(replyRowDiv);
- this.mainContainer = document.getElementById('mainContainer');
this.mainDisplay.style.border = '0px';
this.setFontFace('MingLiu,monospace');
@@ -290,13 +286,6 @@ TermView.prototype = {
this.redraw(false);
},
- prePicRel: function(str) {
- if(str.search(/\.(bmp|gif|jpe?g|png)$/i) == -1)
- return ' type="w"';
- else
- return ' type="p"';
- },
-
redraw: function(force) {
//var start = new Date().getTime();
@@ -332,7 +321,6 @@ TermView.prototype = {
lineChangeds[row] = false;
}
}
-
if (changedLineHtmlStrs.length > 0) {
if (this.useEasyReadingMode) {
if (this.buf.startedEasyReading && this.buf.easyReadingShowReplyText) {
@@ -343,26 +331,16 @@ TermView.prototype = {
this.populateEasyReadingPage();
}
} else {
- while (this.mainContainer.childNodes.length > rows)
- this.mainContainer.removeChild(this.mainContainer.lastChild);
- for (var i = 0; i < changedRows.length; ++i) {
- var row = changedRows[i];
- var component = renderRowHtml(
- changedLineHtmlStrs[i], row, this.chh, false,
- this.mainContainer.childNodes[row]);
- component.setHighlight(
- this.buf.highlightCursor && this.buf.currentHighlighted == row);
- this.displayingRows[row] = component;
- }
+ this.componentScreen = renderScreen(
+ /* For Screen#componentWillReceiveProps */lines.slice(),
+ this.chh,
+ /* showsLinkPreview */false,
+ this.enablePicPreview,
+ this.mainDisplay
+ )
+ this.componentScreen.setCurrentHighlighted(this.buf.highlightCursor && this.buf.currentHighlighted)
}
this.buf.prevPageState = this.buf.pageState;
-
- if (this.enablePicPreview) {
- // hide preview if any update
- renderImagePreview(document.getElementById('imagePreviewContainer'),
- null);
- this.setupPicPreviewOnHover();
- }
}
//var time = new Date().getTime() - start;
//console.log(time);
@@ -373,14 +351,7 @@ TermView.prototype = {
if (this.currentHighlighted == row || this.currentHighlighted === null && row < 0)
return;
console.log('highlight: ' + row);
- if (this.currentHighlighted)
- this.displayingRows[this.currentHighlighted].setHighlight(false);
- if (row >= 0) {
- this.displayingRows[row].setHighlight(true);
- this.currentHighlighted = row;
- } else {
- this.currentHighlighted = null;
- }
+ this.componentScreen.setCurrentHighlighted(row)
},
onInput: function(e) {
@@ -781,37 +752,6 @@ TermView.prototype = {
};
},
- setupPicPreviewOnHover: function() {
- var self = this;
- var aNodes = $([
- ".main a[href^='http://ppt.cc/']",
- ".main a[type='p']",
- ".main a[href^='http://imgur.com/']",
- ".main a[href^='https://imgur.com/']",
- ".main a[href^='http://i.imgur.com/']",
- ".main a[href^='https://i.imgur.com/']",
- ".main a[href^='https://flic.kr/p/']",
- ".main a[href^='https://www.flickr.com/photos/']"
- ].join(",")).not([
- "a[href^='http://imgur.com/a/']",
- "a[href^='https://imgur.com/a/']",
- "a[href^='http://imgur.com/gallery/']",
- "a[href^='https://imgur.com/gallery/']"
- ].join(","));
- var cont = document.getElementById('imagePreviewContainer');
- var onover = function(e) {
- renderImagePreview(cont, this.getAttribute('href'));
- };
- var onout = function(e) {
- renderImagePreview(cont, null);
- };
- for (var i = 0; i < aNodes.length; ++i) {
- var aNode = aNodes[i];
- aNode.addEventListener('mouseover', onover);
- aNode.addEventListener('mouseout', onout);
- }
- },
-
showWaterballNotification: function() {
if (!this.enableNotifications) {
return;
@@ -913,7 +853,6 @@ TermView.prototype = {
clearRows: function() {
this.mainContainer.innerHTML = '';
- this.displayingRows = [];
},
appendRows: function(lines, showsLinkPreview) {
@@ -923,10 +862,9 @@ TermView.prototype = {
el.setAttribute('type', 'bbsrow');
el.setAttribute('srow', this.mainContainer.childNodes.length);
this.mainContainer.appendChild(el);
- var component = renderRowHtml(
+ renderRowHtml(
line, this.mainContainer.childNodes.length, this.chh,
showsLinkPreview, el);
- this.displayingRows.push(component);
}
},
diff --git a/yarn.lock b/yarn.lock
index 2909144f..afc45dd3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -416,6 +416,10 @@ babel-plugin-syntax-async-functions@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95"
+babel-plugin-syntax-class-properties@^6.8.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de"
+
babel-plugin-syntax-exponentiation-operator@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de"
@@ -428,6 +432,10 @@ babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
+babel-plugin-syntax-object-rest-spread@^6.8.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
+
babel-plugin-syntax-trailing-function-commas@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3"
@@ -440,6 +448,15 @@ babel-plugin-transform-async-to-generator@^6.22.0:
babel-plugin-syntax-async-functions "^6.8.0"
babel-runtime "^6.22.0"
+babel-plugin-transform-class-properties@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac"
+ dependencies:
+ babel-helper-function-name "^6.24.1"
+ babel-plugin-syntax-class-properties "^6.8.0"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+
babel-plugin-transform-es2015-arrow-functions@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221"
@@ -623,6 +640,13 @@ babel-plugin-transform-flow-strip-types@^6.22.0:
babel-plugin-syntax-flow "^6.18.0"
babel-runtime "^6.22.0"
+babel-plugin-transform-object-rest-spread@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06"
+ dependencies:
+ babel-plugin-syntax-object-rest-spread "^6.8.0"
+ babel-runtime "^6.26.0"
+
babel-plugin-transform-react-display-name@^6.23.0:
version "6.25.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz#67e2bf1f1e9c93ab08db96792e05392bf2cc28d1"
@@ -780,6 +804,10 @@ balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+base58@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/base58/-/base58-1.0.1.tgz#b8974c171673933e9f95d4866774d00929dc3e09"
+
base64-js@^1.0.2:
version "1.2.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886"