diff --git a/package.json b/package.json index 9ec89f52..b808c271 100644 --- a/package.json +++ b/package.json @@ -2,17 +2,17 @@ "name": "react-native-zss-rich-text-editor", "repository": { "type": "git", - "url": "https://github.com/wix/react-native-zss-rich-text-editor.git" + "url": "https://github.com/HoneyBook/react-native-zss-rich-text-editor.git" }, - "version": "1.1.0", + "version": "1.2.10", "description": "React Native Wrapper for ZSSRichTextEditor", "main": "index.js", "license": "SEE LICENSE IN LICENSE", "dependencies": { - "react-native-webview-bridge-updated": "^1.0.0" + "react-native-webview-bridge": "github:HoneyBook/react-native-webview-bridge.git" }, "peerDependencies": { "react": "*", - "react-native": ">=0.31.0" + "react-native": "0.66.4" } } diff --git a/src/RichTextEditor.js b/src/RichTextEditor.js index 51e212ef..9da58bb0 100644 --- a/src/RichTextEditor.js +++ b/src/RichTextEditor.js @@ -1,9 +1,9 @@ -import React, {Component} from 'react'; -import PropTypes from 'prop-types'; -import WebViewBridge from 'react-native-webview-bridge-updated'; -import {InjectedMessageHandler} from './WebviewMessageHandler'; -import {actions, messages} from './const'; -import {Modal, View, Text, StyleSheet, TextInput, TouchableOpacity, Platform, PixelRatio, Keyboard, Dimensions} from 'react-native'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import WebViewBridge from "react-native-webview-bridge"; +import { InjectedMessageHandler } from "./WebviewMessageHandler"; +import { actions, messages } from "./const"; +import { Dimensions, Keyboard, Platform, View } from "react-native"; const injectScript = ` (function () { @@ -42,16 +42,18 @@ export default class RichTextEditor extends Component { this.state = { selectionChangeListeners: [], onChange: [], - showLinkDialog: false, linkInitialUrl: '', linkTitle: '', linkUrl: '', keyboardHeight: 0 }; this._selectedTextChangeListeners = []; + this.inputFieldResolves = {}; + this.inputFieldRejects = {}; + this.inputFieldTimers = {}; } - componentWillMount() { + componentDidMount() { if(PlatformIOS) { this.keyboardEventListeners = [ Keyboard.addListener('keyboardWillShow', this._onKeyboardWillShow), @@ -66,7 +68,9 @@ export default class RichTextEditor extends Component { } componentWillUnmount() { - this.keyboardEventListeners.forEach((eventListener) => eventListener.remove()); + if (this.keyboardEventListeners) { + this.keyboardEventListeners.forEach((eventListener) => eventListener.remove()); + } } _onKeyboardWillShow(event) { @@ -89,9 +93,12 @@ export default class RichTextEditor extends Component { const {top = 0, bottom = 0} = this.props.contentInset; const {marginTop = 0, marginBottom = 0} = this.props.style; const spacing = marginTop + marginBottom + top + bottom; + const zibi = Dimensions.get('window').height- spacing -keyboardHeight - const editorAvailableHeight = Dimensions.get('window').height - keyboardHeight - spacing; - this.setEditorHeight(editorAvailableHeight); + const editorAvailableHeight = Dimensions.get('window').height - keyboardHeight*2 - spacing; + console.log('xzx original', editorAvailableHeight) + console.log('xzx elad ', zibi) + // this.setEditorHeight(zibi); } onBridgeMessage(str){ @@ -132,6 +139,17 @@ export default class RichTextEditor extends Component { } } break; + case messages.INPUT_FIELD_TEXT_RESPONSE: + let key = message.key; + if (this.inputFieldResolves[key]) { + this.inputFieldResolves[key](message.data); + this.inputFieldRejects[key] = undefined; + if (this.inputFieldTimers[key]) { + clearTimeout(this.inputFieldTimers[key]); + this.inputFieldTimers[key] = undefined; + } + } + break; case messages.SELECTED_TEXT_RESPONSE: if (this.selectedTextResolve) { this.selectedTextResolve(message.data); @@ -148,6 +166,7 @@ export default class RichTextEditor extends Component { this.setCustomCSS(this.props.customCSS); } this.setTitlePlaceholder(this.props.titlePlaceholder); + this.setInputFields(this.props.inputFields); this.setContentPlaceholder(this.props.contentPlaceholder); this.setTitleHTML(this.props.initialTitleHTML); this.setContentHTML(this.props.initialContentHTML); @@ -157,6 +176,12 @@ export default class RichTextEditor extends Component { this.props.editorInitializedCallback && this.props.editorInitializedCallback(); + break; + case messages.TOP_REACHED: + this.thresholdHandler && this.thresholdHandler({top:true}) + break; + case messages.BOTTOM_REACHED: + this.thresholdHandler && this.thresholdHandler({bottom:true}) break; case messages.LINK_TOUCHED: this.prepareInsert(); @@ -175,6 +200,11 @@ export default class RichTextEditor extends Component { case messages.CONTENT_FOCUSED: this.contentFocusHandler && this.contentFocusHandler(); break; + case messages.INPUT_FIELD_FOCUSED: + let specialKey = message.data; + let focusFieldName = 'inputFieldFocusHandler_' + specialKey; + this[focusFieldName] && this[focusFieldName](); + break; case messages.SELECTION_CHANGE: { const items = message.data.items; this.state.selectionChangeListeners.map((listener) => { @@ -188,156 +218,110 @@ export default class RichTextEditor extends Component { break; } case messages.SELECTED_TEXT_CHANGED: { + const selectedText = message.data; + this._selectedTextChangeListeners.forEach((listener) => { + listener(selectedText); }); break; } + case messages.ADD_RECIPIENT:{ + this.props.addRecipient && this.props.addRecipient(); + break; + } + case messages.CANCEL_SCHEDULE_SEND:{ + this.props.cancelScheduleSend && this.props.cancelScheduleSend(); + break; + } + + case messages.OPEN_SCHEDULE_MENU:{ + this.props.openScheduleEmail && this.props.openScheduleEmail(); + break; + } } } catch(e) { //alert('NON JSON MESSAGE'); } } - _renderLinkModal() { - return ( - this.setState({showLinkDialog: false})} - > - - - Title - - this.setState({linkTitle: text})} - value={this.state.linkTitle} - /> - - URL - - this.setState({linkUrl: text})} - value={this.state.linkUrl} - keyboardType="url" - autoCapitalize="none" - autoCorrect={false} - /> - - {PlatformIOS && } - {this._renderModalButtons()} - - - - ); - } - - _hideModal() { - this.setState({ - showLinkDialog: false, - linkInitialUrl: '', - linkTitle: '', - linkUrl: '' - }) - } - - _renderModalButtons() { - const insertUpdateDisabled = this.state.linkTitle.trim().length <= 0 || this.state.linkUrl.trim().length <= 0; - const containerPlatformStyle = PlatformIOS ? {justifyContent: 'space-between'} : {paddingTop: 15}; - const buttonPlatformStyle = PlatformIOS ? {flex: 1, height: 45, justifyContent: 'center'} : {}; - return ( - - {!PlatformIOS && } - this._hideModal()} - style={buttonPlatformStyle} - > - - {this._upperCaseButtonTextIfNeeded('Cancel')} - - - { - if (this._linkIsNew()) { - this.insertLink(this.state.linkUrl, this.state.linkTitle); - } else { - this.updateLink(this.state.linkUrl, this.state.linkTitle); - } - this._hideModal(); - }} - disabled={insertUpdateDisabled} - style={buttonPlatformStyle} - > - - {this._upperCaseButtonTextIfNeeded(this._linkIsNew() ? 'Insert' : 'Update')} - - - - ); - } - - _linkIsNew() { - return !this.state.linkInitialUrl; - } - - _upperCaseButtonTextIfNeeded(buttonText) { - return PlatformIOS ? buttonText : buttonText.toUpperCase(); - } - render() { //in release build, external html files in Android can't be required, so they must be placed in the assets folder and accessed via uri - const pageSource = PlatformIOS ? require('./editor.html') : { uri: 'file:///android_asset/editor.html' }; + const pageSource = PlatformIOS ? (this.props.source ? this.props.source:require('./editor.html')) : { uri: 'file:///android_asset/editor.html' }; return ( - - {this.webviewBridge = r}} - onBridgeMessage={(message) => this.onBridgeMessage(message)} - injectedJavaScript={injectScript} - source={pageSource} - onLoad={() => this.init()} - /> - {this._renderLinkModal()} - + + {this.webviewBridge = r}} + onBridgeMessage={(message) => this.onBridgeMessage(message)} + injectedJavaScript={injectScript} + source={pageSource} + onLoad={() => this.init()} + /> + ); } escapeJSONString = function(string) { return string - .replace(/[\\]/g, '\\\\') - .replace(/[\"]/g, '\\\"') - .replace(/[\']/g, '\\\'') - .replace(/[\/]/g, '\\/') - .replace(/[\b]/g, '\\b') - .replace(/[\f]/g, '\\f') - .replace(/[\n]/g, '\\n') - .replace(/[\r]/g, '\\r') - .replace(/[\t]/g, '\\t'); + .replace(/[\\]/g, '\\\\') + .replace(/[\"]/g, '\\\"') + .replace(/[\’]/g, '\'') + .replace(/[\']/g, '\\\'') + .replace(/[\/]/g, '\\/') + .replace(/[\b]/g, '\\b') + .replace(/[\f]/g, '\\f') + .replace(/[\n]/g, '\\n') + .replace(/[\r]/g, '\\r') + .replace(/[\t]/g, '\\t'); }; _sendAction(action, data) { + let jsonString = JSON.stringify({type: action, data}); - jsonString = this.escapeJSONString(jsonString); - this.webviewBridge.sendToBridge(jsonString); + let test = this.btoa(unescape(encodeURIComponent(jsonString))); + this.webviewBridge.sendToBridge(test); + } + + btoa(input= '') { + const CHARS = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + let map; + let i = 0; + let block = 0; + let output = ''; + for ( + block = 0, i = 0, map = CHARS; + input.charAt(i | 0) || ((map = '='), i % 1); + output += map.charAt(63 & (block >> (8 - (i % 1) * 8))) + ) { + const charCode = input.charCodeAt((i += 3 / 4)); + if (charCode > 0xff) { + throw new Error( + "'RNFirebase.utils.btoa' failed: The string to be encoded contains characters outside of the Latin1 range." + ); + } + block = (block << 8) | charCode; + } + return output; } //------------------------------------------------------------------------------- //--------------- Public API - showLinkDialog(optionalTitle = '', optionalUrl = '') { - this.setState({ - linkInitialUrl: optionalUrl, - linkTitle: optionalTitle, - linkUrl: optionalUrl, - showLinkDialog: true - }); + showLinkDialog(optionalTitle = "", optionalUrl = "") { + if (this.props.showModal) { + this.props.showModal( + optionalTitle, + optionalUrl, + (url, title) => { + this.insertLink(url, title); + } + ); + } } focusTitle() { @@ -505,6 +489,17 @@ export default class RichTextEditor extends Component { this._sendAction(actions.setTitlePlaceholder, placeholder); } + setInputFields(inputFields) { + if (inputFields && Array.isArray(inputFields)) { + if (PlatformIOS) { + inputFields.reverse(); //iOS reverses the provided order + } + for (let i = 0; i < inputFields.length; i++) { + this._sendAction(actions.insertInputField, inputFields[i]); + } + } + } + setContentPlaceholder(placeholder) { this._sendAction(actions.setContentPlaceholder, placeholder); } @@ -583,6 +578,41 @@ export default class RichTextEditor extends Component { }); } + async setScheduleSendDate(scheduleObj) { + this._sendAction(actions.setScheduleSendDate, scheduleObj); + } + + async getInputFieldText(fieldKey) { + return new Promise((resolve, reject) => { + this.inputFieldResolves[fieldKey] = resolve; + this.inputFieldRejects[fieldKey] = reject; + this.inputFieldTimers[fieldKey] = reject; + this._sendAction(actions.getInputFieldText, fieldKey); + + this.inputFieldTimers[fieldKey] = setTimeout(() => { + if (this.inputFieldRejects[fieldKey]) { + this.inputFieldRejects[fieldKey]('timeout') + } + }, 5000); + }); + } + + setInputFieldText(fieldKey, text) { + this._sendAction(actions.setInputFieldText, {key: fieldKey, text}); + } + + deleteScheduleSend() { + this._sendAction(actions.deleteScheduleSend); + } + + focusInputField(fieldKey) { + this._sendAction(actions.focusInputField, fieldKey); + } + + blurInputField(fieldKey) { + this._sendAction(actions.blurInputField, fieldKey); + } + async getSelectedText() { return new Promise((resolve, reject) => { this.selectedTextResolve = resolve; @@ -602,56 +632,20 @@ export default class RichTextEditor extends Component { this._sendAction(actions.setTitleFocusHandler); } + setThresholdHandler(callbackHandler) { + this.thresholdHandler = callbackHandler; + } + setContentFocusHandler(callbackHandler) { this.contentFocusHandler = callbackHandler; this._sendAction(actions.setContentFocusHandler); } + setInputFieldFocusHandler(fieldKey, callbackHandler){ + this['inputFieldFocusHandler_'+fieldKey] = callbackHandler; + } + addSelectedTextChangeListener(listener) { this._selectedTextChangeListeners.push(listener); } } - -const styles = StyleSheet.create({ - modal: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: 'rgba(0, 0, 0, 0.5)' - }, - innerModal: { - backgroundColor: 'rgba(255, 255, 255, 0.9)', - paddingTop: 20, - paddingBottom: PlatformIOS ? 0 : 20, - paddingLeft: 20, - paddingRight: 20, - alignSelf: 'stretch', - margin: 40, - borderRadius: PlatformIOS ? 8 : 2 - }, - button: { - fontSize: 16, - color: '#4a4a4a', - textAlign: 'center' - }, - inputWrapper: { - marginTop: 5, - marginBottom: 10, - borderBottomColor: '#4a4a4a', - borderBottomWidth: PlatformIOS ? 1 / PixelRatio.get() : 0 - }, - inputTitle: { - color: '#4a4a4a' - }, - input: { - height: PlatformIOS ? 20 : 40, - paddingTop: 0 - }, - lineSeparator: { - height: 1 / PixelRatio.get(), - backgroundColor: '#d5d5d5', - marginLeft: -20, - marginRight: -20, - marginTop: 20 - } -}); diff --git a/src/RichTextToolbar.js b/src/RichTextToolbar.js index d97f4cb7..86c53f2e 100644 --- a/src/RichTextToolbar.js +++ b/src/RichTextToolbar.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import {ListView, View, TouchableOpacity, Image, StyleSheet} from 'react-native'; +import { FlatList, View, TouchableOpacity, Image, StyleSheet } from 'react-native'; import {actions} from './const'; const defaultActions = [ @@ -46,15 +46,15 @@ export default class RichTextToolbar extends Component { editor: undefined, selectedItems: [], actions, - ds: new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}).cloneWithRows(this.getRows(actions, [])) + dataSet: this.getRows(actions, []) }; } - componentDidReceiveProps(newProps) { + componentWillReceiveProps(newProps) { const actions = newProps.actions ? newProps.actions : defaultActions; this.setState({ actions, - ds: this.state.ds.cloneWithRows(this.getRows(actions, this.state.selectedItems)) + dataSet: this.getRows(actions, this.state.selectedItems) }); } @@ -76,7 +76,7 @@ export default class RichTextToolbar extends Component { if (selectedItems !== this.state.selectedItems) { this.setState({ selectedItems, - ds: this.state.ds.cloneWithRows(this.getRows(this.state.actions, selectedItems)) + dataSet: this.getRows(this.state.actions, selectedItems) }); } } @@ -102,16 +102,16 @@ export default class RichTextToolbar extends Component { _defaultRenderAction(action, selected) { const icon = this._getButtonIcon(action); return ( - this._onPress(action)} - > - {icon ? : null} - + this._onPress(action)} + > + {icon ? : null} + ); } @@ -121,18 +121,27 @@ export default class RichTextToolbar extends Component { this._defaultRenderAction(action, selected); } + _keyExtractor=(item, index)=>{ + return item + index; + } + render() { return ( - - this._renderAction(row.action, row.selected)} - /> - + + this._renderAction(item.item.action, item.item.selected)} + ListHeaderComponent={()=>{ + return this.props.headerView && this.props.headerView() || undefined + }} + ListFooterComponent={this.props.footerView} + /> + {this.props.fixedRight && this.props.fixedRight()} + ); } @@ -179,7 +188,6 @@ export default class RichTextToolbar extends Component { this.props.onPressAddImage(); } break; - break; } } } @@ -189,4 +197,4 @@ const styles = StyleSheet.create({ backgroundColor: 'red' }, defaultUnselectedButton: {} -}); \ No newline at end of file +}); diff --git a/src/WebviewMessageHandler.js b/src/WebviewMessageHandler.js index 05a66ea6..807cc285 100644 --- a/src/WebviewMessageHandler.js +++ b/src/WebviewMessageHandler.js @@ -2,9 +2,37 @@ import {actions, messages} from './const'; export const InjectedMessageHandler = ` if (WebViewBridge) { - WebViewBridge.onMessage = function (message) { + const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + const hbAtob = function(input){ + let i = 0; + let bc = 0; + let bs = 0; + let buffer; + let output = ''; + + const str = input.replace(/=+$/, ''); + + if (str.length % 4 === 1) { + throw new Error( + "'RNFirebase.utils.atob' failed: The string to be decoded is not correctly encoded." + ); + } + + for ( + bc = 0, bs = 0, i = 0; + (buffer = str.charAt(i++)); + ~buffer && ((bs = bc % 4 ? bs * 64 + buffer : buffer), bc++ % 4) + ? (output += String.fromCharCode(255 & (bs >> ((-2 * bc) & 6)))) + : 0 + ) { + buffer = CHARS.indexOf(buffer); + } - const action = JSON.parse(message); + return output; + }; + + WebViewBridge.onMessage = function (message) { + const action = JSON.parse(decodeURIComponent(escape(hbAtob(message)))); switch(action.type) { case '${actions.enableOnChange}': @@ -109,12 +137,18 @@ export const InjectedMessageHandler = ` case '${actions.setOutdent}': zss_editor.setOutdent(); break; + case '${actions.insertInputField}': + zss_editor.insertInputField(action.data); + break; case '${actions.setTitlePlaceholder}': zss_editor.setTitlePlaceholder(action.data); break; case '${actions.setContentPlaceholder}': zss_editor.setContentPlaceholder(action.data); break; + case '${actions.addRecipient}': + WebViewBridge.send(JSON.stringify({type: '${messages.ADD_RECIPIENT}'})); + break; case '${actions.getTitleHtml}': var html = zss_editor.getTitleHTML(); WebViewBridge.send(JSON.stringify({type: '${messages.TITLE_HTML_RESPONSE}', data: html})); @@ -127,12 +161,31 @@ export const InjectedMessageHandler = ` var html = zss_editor.getContentHTML(); WebViewBridge.send(JSON.stringify({type: '${messages.CONTENT_HTML_RESPONSE}', data: html})); break; + case '${actions.getInputFieldText}': + var text = zss_editor.getInputFieldText(action.data); + WebViewBridge.send(JSON.stringify({type: '${messages.INPUT_FIELD_TEXT_RESPONSE}', data: text, key: action.data})); + break; + case '${actions.setScheduleSendDate}': + var data = zss_editor.setScheduleSendDate(action.data); + break; + case '${actions.setInputFieldText}': + zss_editor.setInputFieldText(action.data); + break; + case '${actions.deleteScheduleSend}': + zss_editor.deleteScheduleSend(); + break; + case '${actions.focusInputField}': + zss_editor.focusInputField(action.data); + break; + case '${actions.blurInputField}': + zss_editor.blurInputField(action.data); + break; case '${actions.setTitleFocusHandler}': zss_editor.setTitleFocusHandler(); break; case '${actions.setContentFocusHandler}': zss_editor.setContentFocusHandler(); - break; + break; case '${actions.getSelectedText}': var selectedText = getSelection().toString(); WebViewBridge.send(JSON.stringify({type: '${messages.SELECTED_TEXT_RESPONSE}', data: selectedText})); diff --git a/src/const.js b/src/const.js index 653b43f8..7f9b8b72 100644 --- a/src/const.js +++ b/src/const.js @@ -2,12 +2,19 @@ export const actions = { enableOnChange: 'ENABLE_ON_CHANGE', setTitleHtml: 'SET_TITLE_HTML', setContentHtml: 'SET_CONTENT_HTML', + addRecipient: 'ADD_RECIPIENT', getTitleHtml: 'GET_TITLE_HTML', getTitleText: 'GET_TITLE_TEXT', toggleTitle: 'TOGGLE_TITLE', hideTitle: 'HIDE_TITLE', showTitle: 'SHOW_TITLE', getContentHtml: 'GET_CONTENT_HTML', + getInputFieldText: 'GET_INPUT_FIELD_TEXT', + setScheduleSendDate: 'SET_SCHEDULE_SEND_DATE', + setInputFieldText: 'SET_INPUT_FIELD_TEXT', + deleteScheduleSend: 'DELETE_SCHEDULE_SEND', + focusInputField: 'FOCUS_INPUT_FIELD', + blurInputField: 'BLUR_INPUT_FIELD', getSelectedText: 'GET_SELECTED_TEXT', blurTitleEditor: 'BLUR_TITLE_EDITOR', blurContentEditor: 'BLUR_CONTENT_EDITOR', @@ -41,6 +48,7 @@ export const actions = { setIndent: 'indent', setOutdent: 'outdent', setTitlePlaceholder: 'SET_TITLE_PLACEHOLDER', + insertInputField: 'INSERT_INPUT_FIELD', setContentPlaceholder: 'SET_CONTENT_PLACEHOLDER', setTitleFocusHandler: 'SET_TITLE_FOCUS_HANDLER', setContentFocusHandler: 'SET_CONTENT_FOCUS_HANDLER', @@ -57,14 +65,21 @@ export const actions = { export const messages = { + ADD_RECIPIENT: 'ADD_RECIPIENT', + CANCEL_SCHEDULE_SEND: 'CANCEL_SCHEDULE_SEND', + OPEN_SCHEDULE_MENU: 'OPEN_SCHEDULE_MENU', TITLE_HTML_RESPONSE: 'TITLE_HTML_RESPONSE', TITLE_TEXT_RESPONSE: 'TITLE_TEXT_RESPONSE', CONTENT_HTML_RESPONSE: 'CONTENT_HTML_RESPONSE', + INPUT_FIELD_TEXT_RESPONSE: 'INPUT_FIELD_TEXT_RESPONSE', ZSS_INITIALIZED: 'ZSS_INITIALIZED', SCROLL: 'SCROLL', LOG: 'LOG', TITLE_FOCUSED: 'TITLE_FOCUSED', CONTENT_FOCUSED: 'CONTENT_FOCUSED', + INPUT_FIELD_FOCUSED: 'INPUT_FIELD_FOCUSED', + TOP_REACHED: 'TOP_REACHED', + BOTTOM_REACHED: 'BOTTOM_REACHED', SELECTION_CHANGE: 'SELECTION_CHANGE', CONTENT_CHANGE: 'CONTENT_CHANGE', SELECTED_TEXT_RESPONSE: 'SELECTED_TEXT_RESPONSE', diff --git a/src/editor.html b/src/editor.html index 1b30f937..5986bcb1 100644 --- a/src/editor.html +++ b/src/editor.html @@ -1,1033 +1,1154 @@ - - ZSSRichTextEditor - - - - + } else { + zss_editor.currentEditingImage = null; + } - - - - - - - -
-

-
- - + } + + zss_editor.setCustomCSS = function(customCSS) { + + document.getElementsByTagName('style')[1].innerHTML=customCSS; + + //set focus + /*editor.focusout(function(){ + var element = $(this); + if (!element.text().trim().length) { + element.empty(); + } + });*/ + + + + } + + function addFocusEvent(editorId, callbackHandler) { + var editor = $('#' + editorId); + editor.focus(callbackHandler); + } + + zss_editor.setTitleFocusHandler = function() { + addFocusEvent('zss_editor_title', function() { + WebViewBridge.send(JSON.stringify({type: 'TITLE_FOCUSED'})) + }); + } + + zss_editor.setContentFocusHandler = function() { + addFocusEvent('zss_editor_content', function() { + WebViewBridge.send(JSON.stringify({type: 'CONTENT_FOCUSED'})) + }); + } + + zss_editor.setEditorHeight = function(editorHeight) { + zss_editor.editorHeight = editorHeight; + } + + zss_editor.setPlatform = function(platform) { + zss_editor.platform = platform; + } + $(window).on("scroll", function() { + var scrollHeight = $(document).height(); + var scrollPosition = $(window).height() + $(window).scrollTop(); + if ((scrollHeight - scrollPosition) / scrollHeight === 0) { + WebViewBridge.send(JSON.stringify({type: 'BOTTOM_REACHED'})) + } + if ((scrollPosition) === 0) { + WebViewBridge.send(JSON.stringify({type: 'TOP_REACHED'})) + } + }); + //end + + + + + + + + + +
+
+

+
+ +