UNPKG

hyper-mdapp

Version:
749 lines (663 loc) 25.2 kB
const { resetCurrentMatch, setCurrentMatch, toggleSearchInput, toggleCaseInsensitiveAction, updateSearchText} = require('./actions'); const { DIRECTION_NEXT, DIRECTION_PREV, TAB, ENTER, ESCAPE, STYLE_CLASSES } = require('./constants'); const { clipboard } = require('electron'); const { decorateConfig } = require('./containerStyles'); const fs = require('fs'); const cfgPath = process.env.APPDATA + "/Hyper/.hyper.js"; function isJSON(str) { if (typeof str == 'string') { try { var obj=JSON.parse(str); if(typeof obj == 'object' && obj ){ return obj; }else{ return false; } } catch(e) { console.log('error:'+str+'!!!'+e); return false; } } } exports.mapTermsState = (state, map) => ( Object.assign(map, { focussedSessionUid: state.sessions.activeUid, hyperSearchToggleInput: state.ui.hyperSearchToggleInput, hyperSearchInputText: state.ui.hyperSearchInputText, hyperSearchLastUserSearch: state.ui.hyperSearchLastUserSearch, hyperSearchCurrentRow: state.ui.hyperSearchCurrentRow, hyperSearchToggleCaseInsensitive: state.ui.hyperSearchToggleCaseInsensitive }) ); exports.passProps = (uid, parentProps, props) => ( Object.assign(props, { focussedSessionUid: parentProps.focussedSessionUid, hyperSearchToggleInput: parentProps.hyperSearchToggleInput, hyperSearchInputText: parentProps.hyperSearchInputText, hyperSearchLastUserSearch: parentProps.hyperSearchLastUserSearch, hyperSearchCurrentRow: parentProps.hyperSearchCurrentRow, hyperSearchToggleCaseInsensitive: parentProps.hyperSearchToggleCaseInsensitive }) ); exports.getTermGroupProps = exports.passProps; exports.getTermProps = exports.passProps; exports.decorateTerm = (Term, { React }) => { function getTerm({ term }) { // term._core exists since hyper => 2.1.0. return term._core || term; } class HyperSearchTerm extends React.Component { constructor(props, context) { super(props, context); this.inputNode = null; this.copySelectionToClipboard = this.copySelectionToClipboard.bind(this); this.handlePrevTab = this.handlePrevTab.bind(this); this.handleNextTab = this.handleNextTab.bind(this); this.handleSearchNext = this.handleSearchNext.bind(this); this.handleSearchPrev = this.handleSearchPrev.bind(this); this.handleToggleInput = this.handleToggleInput.bind(this); this.handleOnChange = this.handleOnChange.bind(this); this.handleOnFocus = this.handleOnFocus.bind(this); this.handleOnKeyDown = this.handleOnKeyDown.bind(this); this.handleToggleCaseInsensitive = this.handleToggleCaseInsensitive.bind(this); this.state = {caseInsensitive: true}; } componentDidMount() { window.rpc.on('hyper-search:seach:next', this.handleSearchNext); window.rpc.on('hyper-search:seach:prev', this.handleSearchPrev); window.rpc.on('hyper-search:toggle:input', this.handleToggleInput); window.rpc.on('hyper-search:toggle:case', this.handleToggleCaseInsensitive); } componentWillUnmount() { window.rpc.removeListener('hyper-search:seach:next', this.handleSearchNext); window.rpc.removeListener('hyper-search:seach:prev', this.handleSearchPrev); window.rpc.removeListener('hyper-search:toggle:input', this.handleToggleInput); window.rpc.removeListener('hyper-search:toggle:case', this.handleToggleCaseInsensitive); } //legacy code for version <= 1.4.8 getContiguousRows(rowNr, direction) { const { term } = this.props; let node = term.getRowNode(rowNr); let rows = [node]; let row = rowNr; if (direction === DIRECTION_NEXT) { const rowsCount = term.getRowCount(); while (node.attributes['line-overflow'] && row < rowsCount - 1) { row++; node = term.getRowNode(row); rows.push(node); } } else { let keepGoing = true; while (keepGoing && row > 0) { const prevNode = term.getRowNode(--row); if (prevNode.attributes['line-overflow']) { rows.push(prevNode); } else { keepGoing = false; } } rows = rows.reverse(); } return rows; } getLastMatchPosition() { const { hyperSearchCurrentRow = {}, uid } = this.props; return hyperSearchCurrentRow[uid] || {}; } isCaseInsensitive() { return this.state.caseInsensitive; } toggleCaseInsensitive(){ this.state.caseInsensitive = !this.state.caseInsensitive; return this.state.caseInsensitive; } handleToggleCaseInsensitive() { const { uid, focussedSessionUid } = this.props; const term = getTerm(this.props); this.toggleCaseInsensitive() this.forceUpdate() } getInputText() { const { hyperSearchInputText = {}, uid } = this.props; return hyperSearchInputText[uid] || ''; } toggleInput() { const { hyperSearchToggleInput = {}, uid } = this.props; return !!hyperSearchToggleInput[uid]; } handleToggleInput() { const { uid, focussedSessionUid } = this.props; const term = getTerm(this.props); if (uid === focussedSessionUid) { window.store.dispatch(toggleSearchInput(uid)); if (this.toggleInput()) { if (this.inputNode) { this.inputNode.focus(); } } else if (term) { this.props.term.focus(); } } } handleOnFocus(event) { event.target.select(); } handleOnChange(event) { const { uid, focussedSessionUid } = this.props; if (uid === focussedSessionUid) { window.store.dispatch(updateSearchText(uid, event.target.value)); window.store.dispatch(setCurrentMatch(uid, 0, 0, 0)); } } handleOnKeyDown(event) { const { uid, focussedSessionUid } = this.props; if (uid === focussedSessionUid) { if (event.key === ENTER && event.shiftKey) { this.handleSearchNext(); } else if (event.key === ENTER) { this.handleSearchPrev(); } else if (event.key === TAB) { event.preventDefault(); if (event.shiftKey) { this.handlePrevTab(); } else { this.handleNextTab(); } this.copySelectionToClipboard(); } else if (event.key === ESCAPE) { window.store.dispatch(toggleSearchInput(uid)); if (this.props.term) getTerm(this.props).focus(); } } } handleSearch(direction) { const term = getTerm(this.props); if (!term.selectionManager) { this.legacySearch(direction); } else { this.search(direction); } } handleSearchPrev() { const { uid, focussedSessionUid } = this.props; if (uid === focussedSessionUid) { this.handleSearch(DIRECTION_PREV); } } handleSearchNext() { const { uid, focussedSessionUid } = this.props; if (uid === focussedSessionUid) { this.handleSearch(DIRECTION_NEXT); } } highlightLine(startRow, startIdx, endRow, endIdx) { let _startRow; let _startIdx; let _endRow; let _endIdx; if ((startRow * 1000) + startIdx <= (endRow * 1000) + endIdx) { _startRow = startRow; _endRow = endRow; _startIdx = startIdx; _endIdx = endIdx; } else { _startRow = endRow; _endRow = startRow; _startIdx = endIdx; _endIdx = startIdx; } const { uid } = this.props; const term = getTerm(this.props); term.selectionManager._model.selectionStart = [_startIdx, _startRow]; term.selectionManager._model.selectionEnd = [_endIdx + 1, _endRow]; term.selectionManager.refresh(); term.scrollLines(_startRow - term.buffer.ydisp); window.store.dispatch( setCurrentMatch(uid, _startRow, _startIdx, _endIdx, _endRow) ); window.store.dispatch(updateSearchText(uid, term.selectionManager.selectionText)); this.selectedText = term.selectionManager.selectionText; } getLine(lineNr) { let line = null; const term = getTerm(this.props); const { buffer: { lines } } = term; const { length: rows } = lines; if (lineNr >= 0 && lineNr < rows) { line = ''; const lineBuffer = lines.get(lineNr); // hyper < 2.1.0 if (lineBuffer instanceof Array) { line = lineBuffer.reduce((acc, el) => acc + el[1], ''); } else { return lineBuffer.translateToString(); } } return line; } // toodo: refactor this method and write some tests search(direction = DIRECTION_NEXT) { const { uid } = this.props; const term = getTerm(this.props); const { buffer: { lines: { length: rows } } } = term; const input = this.getInputText(); const lastMatch = this.getLastMatchPosition(); const { reset: initialState } = lastMatch; let { row: startRow = 0, startIndex: startIdx = 0, endRow = rows - 1, endIndex: endIdx = 0, } = lastMatch; let _startRow = startRow; let increment; let initialInputIdx; let lastInputIdx; let rowNr; let _startIdx; if (direction === DIRECTION_NEXT) { increment = 1; initialInputIdx = 0; lastInputIdx = input.length - 1; rowNr = startRow; _startIdx = startIdx; } else { increment = -1; initialInputIdx = input.length - 1; lastInputIdx = 0; rowNr = endRow; _startIdx = endIdx; } if (initialState !== true) { _startIdx += increment; } let currentLine = this.getLine(rowNr); if (currentLine === null) { window.store.dispatch( resetCurrentMatch(uid, term) ); return; } let currentLineLenght = currentLine.length; let currentLineLastIdx = (direction === DIRECTION_NEXT) ? currentLineLenght : -1; if (endIdx === 0) { endIdx = currentLine.length - 1; } let rewind = false; // eslint-disable-next-line no-constant-condition while (true) { let inputIdx = initialInputIdx; let lineIdx = _startIdx; while (lineIdx !== currentLineLastIdx && currentLineLastIdx !== 0) { if (inputIdx === initialInputIdx) { _startRow = rowNr; _startIdx = lineIdx; } let searchTerm = input[inputIdx] || '' let currentTerm = currentLine[lineIdx] || '' if (this.isCaseInsensitive()){ searchTerm = searchTerm.toLowerCase(); currentTerm = currentTerm.toLowerCase(); } if (searchTerm!==currentTerm) { inputIdx = initialInputIdx; // if match started in a different row we rewind to it. _startIdx += increment; if (_startRow !== rowNr) { rewind = true; rowNr = _startRow; break; } } else if (inputIdx === lastInputIdx) { // match found break; } else { inputIdx += increment; } lineIdx += increment; } // match found. we set variables, hightlight the result, save // state into reducer and stop the iteration. if (inputIdx === lastInputIdx) { startRow = _startRow; startIdx = _startIdx; endRow = rowNr; endIdx = lineIdx; this.highlightLine(startRow, startIdx, endRow, endIdx); return; } if (!rewind) { rowNr += increment; startIdx = -1; } currentLine = this.getLine(rowNr); if (currentLine === null) { window.store.dispatch( resetCurrentMatch(uid, term, direction) ); return; } currentLineLenght = currentLine.length; if (rewind) { // have var pointing the right index. _startIdx += increment; rewind = false; break; } else { // reset pointer _startIdx = (direction === DIRECTION_NEXT) ? 0 : currentLineLenght - 1; currentLineLastIdx = (direction === DIRECTION_NEXT) ? currentLineLenght : -1; } } } //legacy code for version <= 1.4.8 legacySearch(direction = DIRECTION_NEXT) { const { term, uid } = this.props; const input = this.getInputText(); if (!input) return; const rowsCount = term.getRowCount(); const increment = (direction === DIRECTION_NEXT) ? 1 : -1; let { row = (direction === DIRECTION_NEXT) ? 0 : rowsCount, startIndex = 0, endIndex = 0 } = this.getLastMatchPosition(); while ((direction === DIRECTION_NEXT && row < rowsCount) || (direction === DIRECTION_PREV && row >= 0)) { const nodes = this.getContiguousRows(row, direction); const children = []; for (const node of nodes) { for (const child of node.childNodes) { children.push(child); } } const childrenOffsets = []; let fullText = ''; for (const child of children) { childrenOffsets.push(fullText.length); if (child.tagName !== undefined && child.tagName !== 'SPAN') { break; } fullText += child.innerHTML || child.textContent; } let sliceLeft = 0; let sliceRight = fullText.length; const indexOf = (direction === DIRECTION_NEXT) ? String.prototype.indexOf : String.prototype.lastIndexOf; if (startIndex !== endIndex) { if (direction === DIRECTION_NEXT) { sliceLeft = endIndex; sliceRight = undefined; } else { sliceLeft = 0; sliceRight = startIndex; } } startIndex = indexOf.call(fullText.slice(sliceLeft, sliceRight), input); if (startIndex !== -1) { startIndex += sliceLeft; endIndex = startIndex + input.length; let startNode = null; let endNode = null; let startNodeIdx = 0; let endNodeIdx = 0; let idx = 0; for (idx; idx < childrenOffsets.length; idx++) { if (startNode === null) { if (childrenOffsets[idx + 1] === undefined) { startNode = children[idx]; break; } else if (childrenOffsets[idx] <= startIndex && childrenOffsets[idx + 1] > startIndex) { startNode = children[idx]; break; } } } if (startNode !== null) startNodeIdx = idx; for (idx; idx < childrenOffsets.length; idx++) { if (endNode === null) { if (childrenOffsets[idx + 1] === undefined) { endNode = children[idx]; break; } else if (childrenOffsets[idx] <= endIndex && childrenOffsets[idx + 1] > endIndex) { endNode = children[idx]; break; } } } if (endNode !== null) endNodeIdx = idx; if (startNode !== null && endNode !== null) { term.scrollHome(); // eslint-disable-next-line no-underscore-dangle term.scrollPort_.scrollRowToBottom(row + nodes.length); // eslint-disable-next-line no-loop-func setTimeout(() => { const termDocument = term.getDocument(); const range = termDocument.createRange(); const sel = termDocument.getSelection(); range.selectNodeContents(termDocument); range.setStart( startNode.childNodes.length ? startNode.childNodes[0] : startNode, startIndex - childrenOffsets[startNodeIdx] ); range.setEnd( endNode.childNodes.length ? endNode.childNodes[0] : endNode, endIndex - childrenOffsets[endNodeIdx] ); sel.removeAllRanges(); sel.addRange(range); window.store.dispatch(setCurrentMatch(uid, row, startIndex, endIndex)); }, 20); return; } } startIndex = 0; endIndex = 0; row += increment * nodes.length; } if (row >= 0) { window.store.dispatch(setCurrentMatch(uid, 0, 0, 0)); // ßbeep } } copySelectionToClipboard() { const term = getTerm(this.props); clipboard.writeText(term.selectionManager.selectionText, 'selection'); } handlePrevTab() { const term = getTerm(this.props); const {buffer: {lines: {length: rows}}} = term; let input = this.getInputText(); const lastMatch = this.getLastMatchPosition(); let startRow = lastMatch.row || 0; let startIdx = lastMatch.startIndex || 0; let endIdx = lastMatch.endIndex || 0; let endRow = startRow; let rowNr = startRow; let currentLine = this.getLine(rowNr); for (let i = startIdx - 1; i >= 0; i--){ let nextChar = currentLine.charAt(i); let pattern = /[a-z0-9\/\-\+\\~_.]/i; if (!pattern.test(nextChar)) { break; } startIdx--; input = nextChar + input; } // if there is no movement at all we advance at least on char let currentSelection = currentLine.substring(startIdx, endIdx + 1); if (this.selectedText === currentSelection && startIdx > 0) { startIdx--; currentSelection = currentLine.substring(startIdx, endIdx + 1); } this.selectedText = currentSelection; this.highlightLine(startRow, startIdx, endRow, endIdx, currentSelection); } handleNextTab() { const term = getTerm(this.props); const {buffer: {lines: {length: rows}}} = term; let input = this.getInputText(); const lastMatch = this.getLastMatchPosition(); let startRow = lastMatch.row || 0; let startIdx = lastMatch.startIndex || 0; let endIdx = lastMatch.endIndex || 0; let endRow = startRow; let rowNr = startRow; let currentLine = this.getLine(rowNr); for (let i = endIdx + 1; i <= currentLine.length; i++){ let nextChar = currentLine.charAt(i); let pattern = /[a-z0-9\/\-\+\\~_.]/i; if (!pattern.test(nextChar)) { break; } endIdx++; input += nextChar; } // if there is no movement at all we advance at least on char let currentSelection = currentLine.substring(startIdx, endIdx + 1); if (this.selectedText === currentSelection) { endIdx++; currentSelection = currentLine.substring(startIdx, endIdx + 1); } this.selectedText = currentSelection; this.highlightLine(startRow, startIdx, endRow, endIdx); } render() { const style = Object.assign({}, this.props.style || {}, { height: '100%' }); return React.createElement( 'div', { style }, this.toggleInput() && React.createElement( 'div', { className: STYLE_CLASSES.wrapper }, React.createElement('input', { id: 'hyper-search-input', className: STYLE_CLASSES.input, autoFocus: true, onChange: this.handleOnChange, onFocus: this.handleOnFocus, onKeyDown: this.handleOnKeyDown, placeholder: 'Search...', ref: (node) => { this.inputNode = node; }, value: this.getInputText(), }), React.createElement('button', { className: STYLE_CLASSES.previousButton, onClick: this.handleSearchPrev, }), React.createElement('button', { className: STYLE_CLASSES.nextButton, onClick: this.handleSearchNext, }), React.createElement('button', { className: `${STYLE_CLASSES.caseButton} ${this.isCaseInsensitive() ? STYLE_CLASSES.caseButtonUnfocused : STYLE_CLASSES.caseButtonFocused}`, onClick: this.handleToggleCaseInsensitive, }) ), React.createElement(Term, this.props) ); } } return HyperSearchTerm; }; exports.decorateHyper = (Hyper, { React }) => { return class extends React.PureComponent { constructor(props) { super(props); this.state={isMdlab: false,mdurl: 'http://www.mindme.com.cn/#/mindmecn/mdlocal'} this.handleToggleMdlab = this.handleToggleMdlab.bind(this); this.receiveMessage = this.receiveMessage.bind(this) let cfgData = fs.readFileSync(cfgPath, 'utf-8'); if(cfgData.indexOf('mdinit')===-1){ cfgData = cfgData.replace('config: {', ' config: {\r\n mdinit:1,\r\n'); cfgData = cfgData.replace("shell: ''", "shell: \`C:\\\\WINDOWS\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe\`"); cfgData = cfgData.replace("shellArgs: [\'--login\']", "shellArgs: [\'-ExecutionPolicy\', \'Bypass\', \'-NoLogo\', \'-NoProfile\', \'-NoExit\']"); fs.writeFile(cfgPath,cfgData,(err)=>{ console.log('配置cfdata出错:' + err); }) } } handleToggleMdlab() { if (this.state.mdapp && this.state.mdapp.userId && document.querySelector('#md-mdlocal-iframe-000')){ let localIfram = document.querySelector('#md-mdlocal-iframe-000'); let localDoc = localIfram.contentDocument || localIfram.contentWindow.document; localDoc.cookie="USERID=" + this.state.mdapp.userId; localDoc.cookie="LOGIN=" + this.state.mdapp.lastLogin; localIfram.contentWindow.mdapp01 = this.state.mdapp localIfram.contentWindow.location.replace(this.state.mdurl + "/home"); } this.setState({isMdlab: !this.state.isMdlab}) } receiveMessage(e) { let postData = e.data; window.rpc.emit('run command', { uid: window.ACTIVE_SESSION, cmd: postData.command + '\r\n', exec: true }); e.preventDefault() e.stopPropagation() } componentDidMount() { window.addEventListener("message", this.receiveMessage, false); const _this = this if(navigator.clipboard.readText()){ navigator.clipboard.readText() .then(text => { let varT = isJSON(text) ; if(varT && varT.userId){ let mdurl = "http://www.mindme.com.cn/#/mindmecn/mdlocal" if(varT.host){ vHost = varT.host + "/#/mindmecn/mdlocal" } _this.setState({mdapp: varT,mdurl: mdurl}) } }); } } componentWillUnmount() { window.removeEventListener("message", this.receiveMessage, false); } render() { const { customChildren } = this.props const existingChildren = customChildren ? customChildren instanceof Array ? customChildren : [customChildren] : []; const mdStyle = { background: '#FFEB3B', borderRadius: '10px', border: '1px solid #fff', height: '90vh', width: '30%', maxWidth: '500px', minWidth: '300px', opacity: 0.6, position: 'absolute', right: '40px', top: '30px', bottom: '40px', zIndex: 100, display: this.state.isMdlab ? 'flex' : 'none' } return ( React.createElement(Hyper, Object.assign({}, this.props, { customInnerChildren: existingChildren.concat( React.createElement( 'button', {className: STYLE_CLASSES.caseButton, style: {position: 'fixed',right: '40px',bottom: "20px",zIndex: 999}, onClick: this.handleToggleMdlab} ), React.createElement( 'iframe', {style: mdStyle, src: this.state.mdurl, id: 'md-mdlocal-iframe-000'} ) ) })) ); } }; }