diff --git a/base.css b/base.css index d943a76..375e28b 100644 --- a/base.css +++ b/base.css @@ -32,7 +32,7 @@ html, body, div { /* Animate containers to see when they re-render */ :root { /* uncomment for animated container mode */ - /*--debug: none;*/ + --debug: none; } div, ul, li, span, button { diff --git a/debounce.js b/debounce.js new file mode 100644 index 0000000..413e3af --- /dev/null +++ b/debounce.js @@ -0,0 +1,26 @@ +// Debounce with maxWait - ensures execution at least once per wait interval +const debounce = (callback, wait) => { + let timeoutId = null; + let lastExecTime = 0; + + return (...args) => { + const now = Date.now(); + const timeSinceLastExec = now - lastExecTime; + + window.clearTimeout(timeoutId); + + // If enough time has passed since last execution, execute immediately + if (timeSinceLastExec >= wait) { + lastExecTime = now; + callback.apply(null, args); + } else { + // Otherwise, schedule execution for the remaining time + timeoutId = window.setTimeout(() => { + lastExecTime = Date.now(); + callback.apply(null, args); + }, wait - timeSinceLastExec); + } + }; +} + +export default debounce; diff --git a/fsm-actions.html b/fsm-actions.html index 2561b56..76e2d44 100644 --- a/fsm-actions.html +++ b/fsm-actions.html @@ -3,7 +3,7 @@ - + fsm actions diff --git a/fsm-actions.js b/fsm-actions.js index 5dbbbce..9b8608f 100644 --- a/fsm-actions.js +++ b/fsm-actions.js @@ -1,19 +1,7 @@ import StateMachine from './fsm.js'; import StorageProxy from './storage.js'; -import { signal, effect } from './reactive.js'; - -/* -const s = signal(0); -console.log('signal', s); -console.log('s before', s.value); -s.value = 1; -console.log('s after', s.value); - -effect(() => { - console.log('s effect', s.value); -}); -*/ - +import StreamMapper from './streammapper.js'; +import { effect, html, signal } from './reactive.js'; // shorter keys than crypto.randomUUID() const random = () => window.crypto.getRandomValues(new Uint32Array(1))[0]; @@ -30,24 +18,20 @@ const defaultState = { items: [] }; -// Set up storage +const DEBUG = false; + +// Use a unique key for app storage const appKey = 'rfsm-actions'; -const reset = false; -const debug = false; -const storagingState = await StorageProxy(appKey, defaultState, reset, debug); +const clearStorage = false; -/* -for (const k in storagingState) { - console.log('from storage', k, storagingState[k]); -} -*/ +// Initialize storage proxy +const storagingState = await StorageProxy(appKey, defaultState, clearStorage, DEBUG); // Set up signals const appState = {}; for (const k in defaultState) { appState[k] = signal(storagingState[k]); }; -//console.log('appState', appState); // Debounced storage updater // TODO: should be generalized @@ -67,32 +51,41 @@ const updateStorage = () => { const appContainer = document.querySelector('#app'); // State machine for application flow -const appFSM = new StateMachine({ +const machineConfig = { initial: 'uninitialized', states: { uninitialized: ['initialized'], initialized: ['viewing'], - viewing: ['viewing'], + viewing: ['editing'], + editing: ['viewing'] } -}); +}; + +const machine = new StateMachine(machineConfig); /* -// Drives storage writes -appFSM.on('*', (prev) => { - console.log(`FSM.on(*): state changed from ${prev} to ${appFSM.current}, (${appState.current.value})`); - //updateStorage(); +machine.on('*', (prev) => { + //console.log(`FSM.on(*): state changed from ${prev} to ${machine.current}, (${appState.current.value})`); + console.log('FSM.on(*): ', prev, 'to', machine.current); + if (prev !== machine.current) { + console.log('FSM.on(*): changing state', prev, 'to', machine.current); + //appState.current.value = machine.current; + } }); */ -appFSM.on('initialized', (prev) => { +machine.on('initialized', async (prev) => { console.log('FSM.initialized: rendering'); - appContainer.innerHTML= App(); + appContainer.innerHTML = html`${App(appState)}`; console.log('FSM.initialized: rendering started, setting state to viewing'); - appState.current.value = 'viewing'; + //appState.current.value = 'viewing'; + appState.current.value = machine.current; + machine.go(); }); -appFSM.on('viewing', (prev) => { +machine.on('viewing', (prev) => { console.log('FSM.viewing'); + appContainer.innerHTML = html`${App(appState)}`; }); // Item factory @@ -104,21 +97,24 @@ const createItem = (text) => ({ modified: new Date() }); -// Actions - pure functions that modify state +/* + * Actions - pure functions that modify state + * + */ const actions = { addItem: async ({text}) => { if (text.trim()) { const newItem = createItem(text); - appState.items.value = [...appState.items, newItem]; + appState.items.value = [...appState.items.value, newItem]; console.log('updateItem(): setting state to viewing'); - appState.current.value = 'viewing'; + //appState.current.value = 'viewing'; } }, updateItem: async ({item}) => { console.log('actions.updateItem', item); if ('deleted' in item) { - appState.items.value = appState.items.filter(t => t.id != item.id); + appState.items.value = appState.items.value.filter(t => t.id != item.id); console.log('deleted'); } else { @@ -135,86 +131,29 @@ const actions = { console.log('new items', newItems); } console.log('updateItem(): setting state to viewing'); - appState.current.value = 'viewing'; + //appState.current.value = 'viewing'; + }, + + deleteItem: async ({item}) => { + console.log('actions.deleteItem', item); + appState.items.value = appState.items.value.filter(t => t.id != item.id); + console.log('deletedItem(): setting state to viewing'); + //appState.current.value = 'viewing'; } }; -// Receive events from components -// and map them to actions +// Initialize stream mapper - a function customized to the provided +// set of actions, and calls them when matching events are sent to it. -const emit = async (name, data) => { - console.log('emit', name, data); - window.dispatchEvent(new CustomEvent(name, { detail: data })); - console.log('emit complete'); -}; +const listener = async () => { await machine.go() }; -const eventStreamHandler = async (e) => { - console.log('stream event:', e); - const { name, props } = e.detail; - if (name != null) { - console.log('EventStream: invoking action:', name, props); - await actions[name](props); - } - else { - console.error('Unknown event, no matching action:', e.name); - } -}; -window.addEventListener('stream', eventStreamHandler); - - -// Detects state changes and triggers FSM transitions -// TODO: should not have actions setting state directly -// - make an action which trigger FSM? -// - or action calls fsm.go(), and a listener updates signal val? -const stateConductor = (stateMachine, currentStateSignal) => { - //effect(() => { - console.log(`StateConductor() effect prev: ${stateMachine.current}, next: ${currentStateSignal.value}`); - if (stateMachine.current != currentStateSignal.value) { - //console.log('StateConductor: changing state to:', currentStateSignal); - stateMachine.go(currentStateSignal.value).then(() => { - console.log(`StateConductor() transition from ${stateMachine.current} to ${currentStateSignal.value} is complete`); - }); - } - //console.log('State changed to:', currentStateSignal.value); - //}, [currentStateSignal.value]); -}; +const emit = StreamMapper(actions, listener); -// Template processor -const html = (template, ...values) => { - // Create a unique ID for this template instance - const templateId = 'tpl_' + Math.random().toString(36).substr(2, 9); - window.htmlEventHandlers = window.htmlEventHandlers || {}; - - // Process the template and values - let htmlString = ''; - for (let i = 0; i < template.length; i++) { - htmlString += template[i]; - - if (i < values.length) { - const value = values[i]; - - // Check if this value is a function (event handler) - if (typeof value === 'function') { - // Store the function globally with unique ID - const handlerId = 'h_' + Math.random().toString(36).substr(2, 9); - window.htmlEventHandlers[handlerId] = value; - - // Replace with inline handler call - htmlString += `window.htmlEventHandlers.${handlerId}(event)`; - } else if (Array.isArray(value)) { - // Handle arrays (like mapped components) - htmlString += value.join(''); - } else { - // Regular value - htmlString += value; - } - } - } - - return htmlString; -}; +/* + * Components - pure UI functions + * + */ -// Components - pure UI functions const StateInfo = ({ currentState, items }) => { console.log('StateInfo', currentState, items); // State metadata @@ -232,84 +171,106 @@ const Textarea = ({ value }) => { const text = signal(value); const oninput = (e) => { - console.log(`textarea.oninput(): ${text.value} -> ${e.target.value}`); text.value = e.target.value; }; - effect(() => { - console.log('changed', text.value); - }); - return html` `; }; -const Card = ({item}) => { - //console.log('Card', item); +const Button = ({ label, onclick }) => { + const s = signal(label); + return html``; +}; + +const Card = ({ item }) => { + const s = signal(item); const onInput = async (e) => { - const newVal = e.target.value; - console.log('card.onInput:', newVal); - item.text = newVal; - console.log('card.onInput: set'); - await emit('stream', { name: 'updateItem', props: { item }}); - console.log('card.onInput: done'); + s.value = item.text = e.target.value; + emit('updateItem', { item }); + }; + + const onDelete = async (e) => { + emit('deleteItem', { item }); }; return html`
- ${Textarea({value: item.text})} + ${Textarea({value: s.value.text})} + ${Button({label: 'Delete', onclick: onDelete})}
`; }; const Cards = ({items}) => { - //console.log('Cards', items); + const s = signal(items); + return html`
- ${items.map(item => Card({item}))} + ${s.value.map(item => Card({item}))}
`; }; -const App = () => { - //console.log('App', appState); - appState.counter.value = appState.counter.value + 1; - //console.log('currentState', appState.current.value, 'items', appState.items.value); +const App = (state) => { + console.log('App', state); + state.counter.value = state.counter.value + 1; + + const onClickNew = () => emit('addItem', {text: 'New item'}); + + const layout = signal(state.layout.value); + const onClickLayout = () => console.log('Layout change'); + + const sort = signal(state.sort.value); + const onClickSort = () => console.log('Sort change'); + return html` +
+

Reactive FSM Test

+
-

FSM+uHTML Grid (${appState.counter.value})

- ${StateInfo({currentState: appState.current.value, items: appState.items.value})} - ${Cards({items: appState.items.value})} + ${Button({label: '+', onclick: onClickNew})} + ${Button({label: layout.value, onclick: onClickLayout})} + ${Button({label: sort.value, onclick: onClickSort})} + ${StateInfo({currentState: state.current.value, items: state.items.value})} + ${Cards({items: state.items.value})}
+ `; }; -// Initialize signal for current FSM state -appState.current = signal(appFSM.current); - -// Update storage on changes to state -effect(() => { - //console.log('effect() Appstate.current effect:', appState.current.value); - updateStorage(); -}, [appState.current.value]); - // Example data if (appState.items.length == 0) { console.log('ADDING EXAMPLES because no items exist'); // is there a savings in doing this before initializing the fsm? // maybe just confusing //[...Array(3)].map((_, i) => actions.addItem(`item ${i}`)); - const itemCount = 1; + const itemCount = 3; appState.items.value = [...Array(itemCount)].map((_, i) => createItem(`item ${i}`)); console.log('Initial items:', appState.items); } -// Initialized effect that syncs the signal to the state machine -appState.current.value = 'initialized'; -stateConductor(appFSM, appState.current); +// Initialized signal and effect that syncs the signal to the state machine +appState.current = signal(machine.current); + +const delay = f => setTimeout(f, DEBUG ? 2000 : 0); + +// +effect(async () => { + // Writes + // TODO: debounce + updateStorage(); + const next = machineConfig.states[machine.current][0]; + console.log('Dirty flag, next state:', next); +//}, [appState.current.value]); +}, [appState.current.value]); // Fire up the machine +await machine.go(); + console.log('main complete'); diff --git a/fsm.js b/fsm.js index f6b5ed7..d19947e 100644 --- a/fsm.js +++ b/fsm.js @@ -7,51 +7,59 @@ } function FSM(config) { - var events = {}; - var fsm = { + const events = {}; + + // TODO: validate params, eg can't have dead end states, or need some exit func + const fsm = { transitions : config.states, current : config.initial, + next : null, error : config.error, }; - fsm.bind = function(evt, fn) { + fsm.bind = (evt, fn) => { events[evt] = events[evt] || []; events[evt] = events[evt].concat(fn); return fsm; }; - fsm.unbind = function(evt, fn) { + fsm.unbind = (evt, fn) => { if (evt in events && events[evt].indexOf(fn) > -1) events[evt].splice(events[evt].indexOf(fn), 1); return fsm; }; fsm.on = fsm.bind; - function getCbs(key) { - return events[key] || []; - } + const getCbs = (key) => events[key] || []; + + fsm.go = function(next = null) { + const prev = fsm.current; + const params = Array.prototype.slice.call(arguments, 1); - fsm.go = function(next) { - var prev = fsm.current; - var params = Array.prototype.slice.call(arguments, 1); + // Default to first next state for current, so can just call `.go()` + // TODO: file issue + if (next === null) { + next = fsm.transitions[prev][0]; + } if (fsm.transitions[prev].indexOf(next) < 0) return Promise.reject(new ITE(prev, next)); - var after = getCbs('after:' + prev); - var pre = getCbs('before:' + next); - var on = getCbs(next); - var post = getCbs('*'); + const after = getCbs('after:' + prev); + const pre = getCbs('before:' + next); + const on = getCbs(next); + const post = getCbs('*'); + + const beforePost = after.concat(pre, on); - var beforePost = after.concat(pre, on); - function getPrefix(index) { + const getPrefix = (index) => { return (index < after.length ? [next] : index < beforePost.length ? [prev] : [prev, next]); - } + }; - var stateChange = after.length + pre.length; + const stateChange = after.length + pre.length; return beforePost .concat(post, function ensureStateChange() {}) .reduce(function(series, task, index) { - var args = getPrefix(index).concat(params); + const args = getPrefix(index).concat(params); return series.then(function() { if (index === stateChange) { fsm.current = next; diff --git a/reactive.js b/reactive.js index 07cd1e3..bf44a91 100644 --- a/reactive.js +++ b/reactive.js @@ -24,6 +24,7 @@ export function signal(value) { return new Proxy(holder, handler); }; +/* export function reactSignal(value) { const subscriptions = new Set(); @@ -42,6 +43,7 @@ export function reactSignal(value) { return [read, write]; }; +*/ export function effect(fn) { const effect = { @@ -54,3 +56,38 @@ export function effect(fn) { effect.execute(); }; + +// Template processor +export function html (template, ...values) { + // Create a unique ID for this template instance + const templateId = 'tpl_' + Math.random().toString(36).substr(2, 9); + window.htmlEventHandlers = window.htmlEventHandlers || {}; + + // Process the template and values + let htmlString = ''; + for (let i = 0; i < template.length; i++) { + htmlString += template[i]; + + if (i < values.length) { + const value = values[i]; + + // Check if this value is a function (event handler) + if (typeof value === 'function') { + // Store the function globally with unique ID + const handlerId = 'h_' + Math.random().toString(36).substr(2, 9); + window.htmlEventHandlers[handlerId] = value; + + // Replace with inline handler call + htmlString += `window.htmlEventHandlers.${handlerId}(event)`; + } else if (Array.isArray(value)) { + // Handle arrays (like mapped components) + htmlString += value.join(''); + } else { + // Regular value + htmlString += value; + } + } + } + + return htmlString; +}; diff --git a/streammapper.js b/streammapper.js new file mode 100644 index 0000000..23e5e9e --- /dev/null +++ b/streammapper.js @@ -0,0 +1,47 @@ +/* + * StreamMapper - map events in a stream to actions + * + * Parameters + * - string stream key: a topic events are published on + * - object actions: an object where each method modifies state + * + * The event titles are mapped against the actions, and matches are executed. + * + * If no match, emits error. + * + * Receives events from components and maps them to actions, which are then + * executed. + */ + + +// shorter keys than crypto.randomUUID() +const random = () => window.crypto.getRandomValues(new Uint32Array(1))[0]; + +const emit = (key, data) => { + window.dispatchEvent(new CustomEvent(key, { detail: data })); +}; + +export default function(actions, listener) { + + // generate random stream key for this set of actions + const streamKey = random(); + + const eventStreamHandler = async (e) => { + const { name, props } = e.detail; + if (name != null) { + await actions[name](props); + listener?.(name, props); + } + else { + console.error('StreamMapper(): No matching action for event.', e); + } + }; + + // listen for events emitted to it + window.addEventListener(streamKey, eventStreamHandler); + + // return emit function customized for these actions + return (name, props) => { + emit(streamKey, { name, props }); + }; +}; diff --git a/todo.css b/todo.css index 1912679..561869f 100644 --- a/todo.css +++ b/todo.css @@ -95,6 +95,17 @@ div, ul, li, span, button { border-color: var(--button-active-bg); } +textarea { + width: 100%; + padding: 0.75rem; + border: 2px solid var(--border-color); + border-radius: 4px; + font-size: 1rem; + background: var(--input-bg); + color: var(--text-color); + box-sizing: border-box; +} + .controls { margin-bottom: 2rem; display: flex; @@ -102,7 +113,7 @@ div, ul, li, span, button { flex-wrap: wrap; } -.controls button { +button { padding: 0.5rem 1rem; border: 1px solid var(--border-color); background: var(--button-bg); @@ -112,11 +123,11 @@ div, ul, li, span, button { font-size: 0.9rem; } -.controls button:hover { +button:hover { background: var(--button-hover); } -.controls button.active { +button.active { background: var(--button-active-bg); color: var(--button-active-text); border-color: var(--button-active-bg); diff --git a/vi.css b/vi.css new file mode 100644 index 0000000..a2c85e6 --- /dev/null +++ b/vi.css @@ -0,0 +1,44 @@ +.editor { + color: #aaa; + background-color: #000; + position: absolute; + top: 0px; + left: 0px; + display: block; + overflow: hidden; + width: 100%; + height: 100%; + cursor: default; + font-family: monospace; + font-size: 100%; +} +.print, .editor, .editorcursor { display: none; } +@media print { + .editor { display:none !important; } + html,body { overflow:auto !important; } + .editorcursor { display:none !important; } + .print {position:absolute;top:0;left:0;display:block !important;font-family:monospace;height:100%;} + .editortools {display:none !important;} + textarea,input { display:none !important;} +} +/* +.editor { + color: #333; + background-color: #eee; +} + +html,body { + margin: 0; + padding: 0; + height: 100%; +} +.print, .editor, .editorcursor { display: none; } +@media print { + .editor { display:none !important; } + html,body { overflow:auto !important; } + .editorcursor { display:none !important; } + .print {position:absolute;top:0;left:0;display:block !important;font-family:monospace;height:100%;} + .editortools {display:none !important;} + textarea,input { display:none !important;} +} +*/ diff --git a/vi.js b/vi.js new file mode 100644 index 0000000..e59c2f4 --- /dev/null +++ b/vi.js @@ -0,0 +1,3939 @@ +/**@license + * JSVI - VI in JavaScript. + * Copyright (C) 2006-2008 Internet Connection, Inc. + * Copyright (C) 2013-2018 Jakub Jankiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* TODO + +sortof bugs: + the toolbar is kind of ugly- possibly replace with an image-version- + or more likely- replace with styled buttons + +ideas: + uneditable sections? + + a ruler at the 72 character mark? possibly word-wrapping? + + want to switch between vi/emacs modes? + +*/ +var vi = (function() { + + // Browser detection from jQuery 1.8.3 + var browser = (function() { + var matched, browser; + + var uaMatch = function( ua ) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || + /(webkit)[ \/]([\w.]+)/.exec( ua ) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || + /(msie) ([\w.]+)/.exec( ua ) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + + matched = uaMatch( navigator.userAgent ); + browser = {}; + + if ( matched.browser ) { + browser[ matched.browser ] = true; + browser.version = matched.version; + } + + // Chrome is Webkit, but Webkit is also Safari. + if ( browser.chrome ) { + browser.webkit = true; + } else if ( browser.webkit ) { + browser.safari = true; + } + return browser; + })(); + + + var emacsen = false; + var term_cols; + var term_rows; + var term_win_width; + var term_win_height; + var term_cur_width; + + var suggest; + var backing; + + var fakemode; + + var tagstyle = 0; + var line_height = 0; + var cclick = undefined; + + var mode = 0; + var accum = 0; + var lastaccum = 0; + var marks = new Object(); + var registers = new Object(); + var lastreg = ''; + + var lastcommand; + var lastmotion; + var cursoriv; + var drawiv; + + var term; + var base = 0; + var left = 0; + + var spell_script; + + var vselm = 0; + var vselx = undefined; + var vsely = undefined; + var vseld = false; + + var lastkey = undefined; + + var statustext = ''; + var command = ''; + var oldcommand = ''; + var commandleft = 0; + var savex, savey; + var once = true; + + var cursorx, cursory; + var file = new Array(); + var tags = new Array(); + var palette; + var cursor; + + var yank_buffer = undefined; + + var term_save_h = new Array(); + + var term_save_ss; + var term_save_mc; + var term_save_kd; + var term_save_kp; + var term_save_rs; + var term_save_op; + + var lastsearch; + var lastsubst; + var lastflags; + + var undoset = undefined; + var undoline = undefined; + var undoy = -1; + + var term_ex_motion; + var vi_ft_reg; + var viflags = ''; + var lastinsert = ''; + + var spelling = false; + var spellcheck = new Object(); + var safewords = new Object(); + var brokenwords = new Object(); + var suggestions = new Object(); + + var onSave, onExit; + + safewords['jsvi']=true; + safewords['javascript']=true; + safewords['URL']=true; + safewords['hyperlinks']=true; + safewords['HTML']=true; + safewords['UNIX']=true; + safewords['Firefox']=true; + safewords['MSIE']=true; + safewords['Ctrl']=true; + safewords['vi']=true; + safewords['vi-keys']=true; + safewords[':hardcopy']=true; + safewords['unicode-aware']=true; + safewords['developer-centric']=true; + + var doing_backing_paste = false; + + function _true() { return true; } + function _false() { return false; } + + function _cbrestore() { + var i; + for (i = 0; i < term_save_h.length; i++) { + var z = term_save_h[i]; + z(); + } + term_save_h = new Array(); + } + function _cbw(nx,ny) { + var nz = window['on'+nx]; + (function(x,y,z) { + term_save_h[term_save_h.length] = function() { + window['on'+x] = z; + }; + })(nx,ny,nz); + window['on'+nx]=ny; + } + function _cbd(nx,ny) { + var nz = document['on'+nx]; + (function(x,y,z) { + term_save_h[term_save_h.length] = function() { + document['on'+x] = z; + }; + })(nx, ny, nz); + document['on'+nx]=ny; + } + function _rer(re,t,aa) { + if (RegExp.rightContext != undefined) return RegExp.rightContext; + // emulate rightContext + var s = re.toString(); + if ((s.substr(0,2) == '/^' && s.substr(s.length-1,1) == '/') || s.substr(0,1) == '^') { + // bound at beginning + return t.substr(aa[0].length, t.length - aa[0].length); + } + if ((s.substr(0,1) == '/' && s.substr(s.length-2,2) == '$/') || s.substr(s.length-1,1) == '$') { + // bound at end + return ""; + } + var j = t.lastIndexOf(aa[0]); + if (j != -1) return t.substr(j.t.length-j); + return t; + + } + function _rel(re,t,aa) { + if (RegExp.leftContext != undefined) return RegExp.leftContext; + // emulate leftContext + var s = re.toString(); + if ((s.substr(0,2) == '/^' && s.substr(s.length-1,1) == '/') || s.substr(0,1) == '^') { + // bound at beginning + return ""; + } + if ((s.substr(0,1) == '/' && s.substr(s.length-2,2) == '$/') || s.substr(s.length-1,1) == '$') { + // bound at end + return t.substr(0, t.length - aa[0].length); + } + var j = t.indexOf(aa[0]); + if (j != -1) return t.substr(0,j); + return ""; + } + function _hra(x) { + var i; + var cx = 0; + var t = ''; + var g = ''; + for (i = 0; i < x.length; i++) { + var x3 = x.substr(i,3); + var gx = String.fromCharCode(cx); + if (x3 == '') { + cx = cx | 1; + i += 2; + } else if (x3 == '') { + cx = cx | 2; + i += 2; + } else if (x3 == '') { + cx = cx | 16; + i += 2; + } else if (x3 == ' + cx = cx | 4; + i += 16; + } else if (x3 == ' + cx = (cx | 4) ^ 4; + i += 6; + } else if (x3 == '&am') { // & + t += '&'; + g += gx; + i += 4; + } else if (x3 == '<') { // < + t += '<'; + g += gx; + i += 3; + } else { + t += x.substr(i,1); + g += gx; + } + } + var aa = new Array(); + aa[0] = t; + aa[1] = g; + return aa; + } + function _rtf(t,g) { + var cx = 0; + var i; + var o = ''; + if (t == undefined) { + t = ''; + g = ''; + } + for (i = 0; i < t.length; i++) { + var gx = g.substr(i, 1).charCodeAt(0); + var tx = t.substr(i, 1); + if (tx == "<") tx = "<"; + else if (tx == '&') tx = '&'; + if (gx != cx) { + if ((gx & 1) && !(cx & 1)) { + o += ""; + } else if (!(gx & 1) && (cx & 1)) { + o += ""; + } + + if ((gx & 2) && !(cx & 2)) { + o += ""; + } else if (!(gx & 2) && (cx & 2)) { + o += ""; + } + + if ((gx & 4) && !(cx & 4)) { + o += ""; + } else if (!(gx & 4) && (cx & 4)) { + o += ""; + } + if ((gx & 16) && !(cx & 16)) { + o += ""; + } else if (!(gx & 16) && (cx & 16)) { + o += ""; + } + cx = gx; + } + o += tx; + } + return o; + } + function _rtfl(y) { + return _rtf(file[y],tags[y]); + } + function _dfx(q) { + /*@cc_on @*/ + /*@if (1) return + @end @*/ + q.style.position = 'fixed'; + } + function term_freeze() { + var i; + var o = ''; + for (i = 0; i < file.length; i++) { + o += _rtfl(i)+"\n"; + } + return o; + } + function term_thaw(s) { + var a = s.split("\n"); + var i; + var aa; + file = new Array(); + tags = new Array(); + var o = ''; + for (i = 0; i < a.length; i++) { + aa = _hra(a[i]); + file[i] = aa[0]; + tags[i] = aa[1]; + } + } + + function _mxo(z,y) { + var i; + var o = ''; + for (i = 0; i < z.length; i++) { + o += String.fromCharCode(z.substr(i, 1).charCodeAt(0) | y); + } + return o; + } + function _mxs(n,y) { + var z = String.fromCharCode(y); + var i; + var o = ''; + for (i = 0; i < n; i++) { + o += z; + } + return o; + } + function _zeros(n) { + return _mxs(n,0); + } + function _fauc() { + var d = document.getElementsByTagName('A'); + var i; + for (i = 0; i < d.length; i++) { + var j = d[i]; + if (j._len && j._term) { + if (j._row == (base+cursory) && (left+cursorx) >= j._col && (left+cursorx) <= (j._col+j._len)) { + return j; + } + } + } + return undefined; + } + function _pass_click(e) { + var z = _fauc(); + if (z && z.onclick) return z.onclick(); + return false; + } + function _pass_dblclick(e) { + var z = _fauc(); + if (z && z.ondblclick) return z.ondblclick(); + return false; + } + + function _cancel_ev(e) { + if (!e) e = window.event; + if (!e) return false; + if (e.preventDefault) e.preventDefault(); + if (e.stopPropagation) e.stopPropagation(); + return false; + } + function _willclick(e) { + if (window.event) { + if (!e) e = window.event; + } + if (!e) return true; + if (cclick != undefined) window.clearTimeout(cclick); + var x = e.clientX; + var y = e.clientY; + cclick = window.setTimeout(function() { + cclick=undefined; + _cursortoxy(x,y); + }, 200); + return false; + } + function _subclick(e) { + return _willclick(e); + } + function _srep(e) { + if (!e) e = window.event; + if (e.preventDefault) e.preventDefault(); + if (e.stopPropagation) e.stopPropagation(); + e.cancelBubble=true; + var y = this._row; + var x = this._col; + var len = this._len; + var rep = this._word; + var t = (file[y]); + var g = (tags[y]); + var w = (t.substr(x, len)); + var st = (g.substr(x, len)); + while (st.length < rep.length) { + st = st + st; + } + if (st.length > rep.length) { + st = st.substr(0, rep.length); + } + + term_save_undo(); // save undo! + file[y] = t.substr(0, x) + rep + t.substr(x+len, t.length-(x+len)); + tags[y] = g.substr(0, x) + st + g.substr(x+len, t.length-(x+len)); + suggest.style.display = 'none'; + suggest._visible = false; + while (suggest.firstChild) suggest.removeChild(suggest.firstChild); + if (w == rep) { + statustext = ''; + } else { + statustext = 'Replaced "' + w + '" with "' + rep + '"'; + } + term_redraw(); + return false; + } + function _rl(w,h) { + var x = document.createElement('DIV'); + x.style.overflow = 'hidden'; + x.style.height = h + 'px'; + x.style.marginLeft = w + 'px'; + x.style.marginRight = w + 'px'; + x.style.backgroundColor = palette[0]; + x.style.display = 'block'; + x.style.innerHTML = ' '; + x.style.fontFamily = 'monospace'; + return x; + } + function _ruo(t) { + this.style.color = palette[0]; + this.style.backgroundColor = palette[1]; + } + function _rux(t) { + this.style.color = palette[1]; + this.style.backgroundColor = palette[0]; + } + function _openurl(e) { + var u = this._term; + window.open(u,'_new'); + return true; + } + function _suggest(e) { + var z = this; + if (window.event) { + if (!e) e = window.event; + } + if (e) { + if (e.preventDefault) e.preventDefault(); + if (e.stopPropagation) e.stopPropagation(); + e.cancelBubble=true; + } + (function(q) { + window.setTimeout(function(){ + _dosuggest(q); + }, 10); + })(z); + return false; + } + function _dosuggest(z) { + var x = 0; + var y = 0; + var xt = z._term; + var wrow = z._row; + var wcol = z._col; + var wlen = z._len; + while (z && z != document.body) { + x += z.offsetLeft; + y += z.offsetTop; + z = z.offsetParent; + } + + suggest._visible = true; + suggest.style.top = y + 'px'; + suggest.style.left = x + 'px'; + suggest.style.display = 'block'; + suggest.style.zIndex = '3'; + suggest.style.padding = '2px'; + + while (suggest.firstChild) suggest.removeChild(suggest.firstChild); + + var sg = document.createElement('DIV'); + sg.style.backgroundColor = palette[0]; + sg.style.color = palette[1]; + sg.style.fontSize = '100%'; + sg.style.padding = '2px'; + sg.style.textAlign = 'center'; + sg.style.cursor = 'default'; + + var sa = suggestions[xt]; + var i; + var fs = 200; + var fd = parseInt((100 / sa.length) * 1.5); + var bf; + for (i = 0; i < sa.length; i++) { + var da = document.createElement('A'); + da.href = 'javascript:void(0)'; + da.onclick = _srep; + da._word = sa[i]; + da._row = wrow; + da._col = wcol; + da._len = wlen; + da.onmouseover = _ruo; + da.onmouseout = _rux; + da.style.margin = '4px'; + da.style.textDecoration = 'none'; + da.style.display = 'block'; + da.style.color = palette[1]; + da.style.backgroundColor = palette[0]; + da.style.fontSize = fs + '%'; + fs -= fd; + if (fs <= 100) fs = 100; + da.appendChild(document.createTextNode(sa[i])); + // err... + da.appendChild(document.createElement('BR')); + if ((wrow-base) > term_rows-(sa.length+1)) { + sg.insertBefore(da, sg.firstChild); + bf = true; + } else if (((i % 2) == 0) || (wrow < (sa.length+1))) { + sg.appendChild(da); + } else { + sg.insertBefore(da, sg.firstChild); + } + da = undefined; // break + } + if (sa.length == 0) { + sg.appendChild(document.createTextNode('No matches')); + } + var da = document.createElement('A'); + da.href = 'javascript:void(0)'; + da.onclick = _srep; + da._word = xt; + da._row = wrow; + da._col = wcol; + da._len = wlen; + da.style.margin= '4px'; + da.style.textDecoration = 'none'; + da.style.color = palette[1]; + da.style.backgroundColor = palette[0]; + da.style.borderBottom = '1px dashed red'; + da.style.fontSize = '100%'; + da.onmouseover = _ruo; + da.onmouseout = _rux; + da.appendChild(document.createTextNode(xt)); + if (wrow > term_rows-(sa.length+1)) { + sg.insertBefore(da, sg.firstChild); + } else { + sg.appendChild(da); + } + + // rounded top and bottom + suggest.appendChild(_rl(3,1)); + suggest.appendChild(_rl(2,1)); + suggest.appendChild(_rl(1,2)); + suggest.appendChild(sg); + suggest.appendChild(_rl(1,2)); + suggest.appendChild(_rl(2,1)); + suggest.appendChild(_rl(3,1)); + + // msie needs to recalculate these things manually... grr... + // this doesn't work because msie doesn't calculate offsetwidth (thtphtpht) + var zq; + var mw = 11; + if (mw < xt.length) mw = xt.length; + for (i = 0; i < sa.length; i++) { + if (mw < sa[i].length) mw = sa[i].length; + } + + if (mw) { + mw *= (term_cur_width*2); + mw += 16; + suggest.style.width = mw + 'px'; + } + + var sx = parseInt(sg.offsetWidth / 4); + if (x < sx) { + x = 0; + } else { + x -= sx; + suggest.style.left = x + 'px'; + } + var sy = parseInt(sg.offsetHeight / 4); + if (bf) { + suggest.style.top = ''; + suggest.style.bottom = '0px'; + } else if (y < sy) { + y = 0; + suggest.style.top = '0px'; + suggest.style.bottom = ''; + } else { + y -= sy; + suggest.style.top = y + 'px'; + suggest.style.bottom = ''; + } + + statustext = 'Suggestions for: ' + xt; + term_redraw(); + + da = undefined; // break + sg = undefined; // break + } + + function _backing_paste_real() { + doing_backing_paste = false; + term_redraw(); + if (!backing.value) return; + if (backing._lastvalue == backing.value) { + return; + } + backing._lastvalue = backing.value; + term_paste(false, backing.value); + term_redraw(); + } + function _msie_paste() { + var chunk = "new content associated with this object"; + event.returnValue = false; + term_paste(false, window.clipboardData.getData("Text", chunk)); + } + function _backing_paste() { + _update_backing(); + if (!doing_backing_paste) { + doing_backing_paste = true; + window.setTimeout(_backing_paste_real, 10); + } + } + function _update_backing() { + if (!backing) return; + /*@cc_on @*/ + /*@if (1) return + @end @*/ + backing.focus(); + backing.select(); + } + function _yaty(y) { + if (line_height) return parseInt(y/line_height); + var zx; + var qx = term.firstChild; + var nh = 0; + while (qx && qx != document.body) { + nh += qx.offsetTop; + qx = qx.offsetParent; + } + + var ny = 0; + var cy = 0; + for (zx = term.firstChild; zx; zx = zx.nextSibling) { + nh += (zx.offsetHeight + 4); + if (y <= nh) { + cy = ny; + break; + } + ny++; + } + return cy; + } + function _cursortoxy(x,y) { + // this is a little gross... + var sx = cursorx; + cursorx = parseInt(x / term_cur_width); + term_redraw(); + + var sy = cursory; + cursory = _yaty(y); + + if (cursory >= (term_rows-1)) { + cursory = sy; + cursorx = sx; + sy = 0; + } + + term_scrollto(); + term_calcx(); + if (cursory != sy) { + term_redraw(); + } else { + term_calcy(); + } + _update_backing(); + return true; + } + function _mousescroll(e) { + if (!e) e = window.event; + var d = 0; + if (e.wheelDelta) { + d = e.wheelDelta; + if (d < 0) d = 1; else d = -1; + } else if (e.detail) { + d = e.detail; + } else { + return true; + } + if (d < 0) { + if (base > 0) base--; + } else if (d > 0) { + if (base < (file.length - (term_rows-1))) base++; + } + term_redraw(); + return false; + } + + function _mousedown(e) { + if (suggest._visible) return true; + if (!e) e = window.event; + var y = _yaty(e.clientY); + if (y >= (term_rows-1)) return true; + _willclick(e); + vseld = true; + vselm = 0; + vselx = undefined; + vsely = undefined; + return false; + } + function _mousemove(e) { + if (!e) e = window.event; + if (e) { + if (e.preventDefault) e.preventDefault(); + if (e.stopPropagation) e.stopPropagation(); + e.cancelBubble=true; + } + if (window.getSelection) { + var s = window.getSelection(); + if (s.removeAllRanges) s.removeAllRanges(); + } + if (document.selection && document.selection.empty) { + eval('try{document.selection.empty();}catch(e){}'); + } + // fixup selection + _update_backing(); + + if (suggest._visible || !vseld) return true; + if (vseld) { + if (vselx == undefined && vsely == undefined) { + // turn on v here + vselm = 1; + vselx = cursorx+left; + vsely = cursory+base; + } + _willclick(e); + return true; + } + return true; + } + function _mouseup(e) { + if (suggest._visible || !vseld) return true; + vseld = false; + if (vselm && vselx != undefined && vsely != undefined) { + // okay, we HAVE selection + var rr = lastreg; + lastreg = '*'; + term_vi_set('y'); + term_vi_unset('d'); + term_select(); + term_operate(); + lastreg = rr; + } else { + vselm = 0; + } + _willclick(e); + return false; + } + function _mouseclick(e) { + if (!e) e = window.event; + var y = _yaty(e.clientY); + if (y >= (term_rows-1)) return true; + vselm = 0; + _cursorto(e); + return true; + } + function _cursorto(e) { + if (!e) e = window.event; + var x = e.clientX; + var y = e.clientY; + if (suggest._visible) { + suggest.style.display = 'none'; + suggest._visible = false; + statustext = ''; + } + return _cursortoxy(x,y); + } + + function _word(s) { + var t = s.replace(/[.?!,:]*$/,""); + if (t) s = t; + t = s.replace(/[ \r\n\t]/,""); + if (t) s = t; + return s; + } + function _safe(s) { + if (s.match(/^[ \r\n\t]*<.*>[ \r\n\t]*$/)) return true; + return false; + } + function _xhttp() { + var xmlhttp=false; + /*@cc_on @*/ + /*@if (@_jscript_version >= 5) + try { + xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); + } catch (e) { + try { + xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); + } catch (E) { + xmlhttp = false; + } + } + @end @*/ + if (!xmlhttp && typeof XMLHttpRequest!='undefined') { + xmlhttp = new XMLHttpRequest(); + } + if (!xmlhttp) return new Object(); // fake out caller + return xmlhttp; + } + + function term_setmode(n) { + mode = n; + lastinsert = ''; + } + + function term_roll_yank() { + registers["9"] = registers["8"]; + registers["8"] = registers["7"]; + registers["7"] = registers["6"]; + registers["6"] = registers["5"]; + registers["5"] = registers["4"]; + registers["4"] = registers["3"]; + registers["3"] = registers["2"]; + registers["2"] = registers["1"]; + registers["1"] = registers["0"]; + + if (lastreg == '0') { + registers[""] = yank_buffer; + } + if (lastreg == "" || lastreg == "*") { + backing._lastvalue = yank_buffer; + backing.value = yank_buffer; + } + registers["0"] = yank_buffer; + } + + function term_justify() { + var y = cursory+base; + var i; + if (!accum) accum = 1; + var t = (file[y]); + file[p] = t.replace(/[ ][ ]*$/,""); + tags[p] = tags[p].substr(0, file[p].length); + for (i = 0; i < accum; i++) { + var t = file[i+y+1]; + var g = tags[i+y+1]; + term_delete(i+y+1); // ignore return + + t = t.replace(/^[ ][ ]*/,""); + g = g.substr(g.length - t.length, t.length); + if (t != '') { + file[y] = file[y] + " " + t; + tags[y] = tags[y] + "\0" + g; + } + } + file[y] = file[y].replace(/^[ ][ ]*/,""); + tags[y] = tags[y].substr(tags[y].length - file[y].length, file[y].length); + accum = 0; + } + + function term_vi_bb() { + return term_skipbackward(/[ ][^ ][^ ]*[ ]*$/); + } + function term_vi_b() { + return term_skipbackward(/[^a-zA-Z0-9_][a-zA-Z0-9_][a-zA-Z0-9_]*[^a-zA-Z0-9_]*$/); + } + function term_vi_tt() { + var t = (file[cursory+base]); + var i; + var w = term_cols; + for (i = (cursorx+left)-1; i >= 0; i--) { + var c = (t.substr(i, 1)); + if (c == vi_ft_reg) { + cursorx = (i - left)+1; + if (cursorx >= w) { + left = 0; + cursorx = i+1; + } + return true; + } + } + return false; + } + function term_vi_t() { + var t = (file[cursory+base]); + var i; + for (i = (cursorx+left)+1; i < t.length; i++) { + var c = (t.substr(i, 1)); + if (c == vi_ft_reg) { + cursorx = (i - left)-1; + if (term_vi_flag('d') || term_vi_flag('c') || term_vi_flag('y')) cursorx++; + if (cursorx < 0) { + left = 0; + cursorx = i-1; + } + return true; + } + } + return false; + } + function term_vi_ff() { + var t = (file[cursory+base]); + var i; + for (i = (cursorx+left)-1; i >= 0; i--) { + var c = (t.substr(i, 1)); + if (c == vi_ft_reg) { + cursorx = i - left; + if (cursorx < 0) { + left = 0; + cursorx = i; + } + return true; + } + } + return false; + } + function term_vi_f() { + var t = (file[cursory+base]); + var i; + var w = term_cols; + for (i = (cursorx+left)+1; i < t.length; i++) { + var c = (t.substr(i, 1)); + if (c == vi_ft_reg) { + cursorx = i - left; + if (term_vi_flag('d') || term_vi_flag('c') || term_vi_flag('y')) cursorx++; + if (cursorx >= w) { + left = 0; + cursorx = i; + } + return true; + } + } + return false; + } + function term_vi_eof() { + cursorx = 0; + left = 0; + base = 0; + if (accum) { + cursory = accum-1; + return false; + } else { + base = file.length - (term_rows-1); + cursory = term_rows-1; + if (base < 0) { + base = 0; + cursory = file.length-1; + } + } + term_redraw(); + return true; + } + function term_vi_top() { + cursorx = 0; + base = 0; + left = 0; + if (accum) { + cursory = accum-1; + return false; + } else { + cursory = 0; + } + return true; + } + function term_vi_h() { cursorx--; term_scrollto(); return true; } + function term_vi_j() { cursory++; term_scrollto(); return true; } + function term_vi_k() { cursory--; term_scrollto(); return true; } + function term_vi_l() { + cursorx++; + // hack + var a = mode; + mode = 1; + term_scrollto(); + mode = a; + return true; + } + function term_vi_ll() { + cursory = term_rows-2; + return true; + } + function term_vi_mm() { + cursory = parseInt((term_rows-2) / 2); + cursorx = 0; left = 0; + return true; + } + function term_vi_hh() { + cursory = 0; + return true; + } + function term_vi_ob() { + cursory--; + return term_skipreverse2(/^[ ]*$/, 0); + } + function term_vi_cb() { + cursory++; + return term_skipforward(/^[ ]*$/, 0); + } + function term_vi_ww() { + return term_skipforward(/[ ][^ ]/, 0); + } + function term_vi_w() { + return term_skipforward(/[^a-zA-Z0-9_(){}<>][a-zA-Z0-9_(){}<>]/, 0); + } + + function term_vi_flag(f) { + return (viflags.indexOf(f) == -1) ? false : true; + } + function term_vi_unset(f) { + var j = viflags.indexOf(f); + if (j == -1) return; + viflags = viflags.substr(0, j) + viflags.substr(j+1, (viflags.length-j)-1); + } + function term_vi_set(f) { + if (viflags.indexOf(f) == -1) viflags += ''+f; + } + function term_vi_bounce() { + var y = cursory+base; + var t = (file[y]); + var x = cursorx+left; + var z1 = '[{()}]'; + var z2 = ']})({['; + while (x < t.length) { + var z = z1.indexOf(t.substr(x,1)); + if (z == -1) { + x++; + continue; + } + var d = (z > 2) ? -1 : 1; + var c=z2.substr(z,1); + while (y >= 0 && y < file.length) { + while (x > 0 && x < t.length) { + if (t.substr(x,1) == c) { + cursorx = x-left; + cursory = y; + base = 0; + term_scrollto(); + return true; + } + x += d; + } + if (d == -1) { + y--; + t = file[y]; + x = (t.length-1) + } else { + y++; + t = file[y]; + x = 0; + } + } + } + return false; + } + function term_vi_eol() { + var t = (file[cursory+base]); + cursorx = (t.length)-left; + if (cursorx < 0) { + left = 0; + cursorx = (t.length); + } + return true; + } + function term_vi_line() { + var t = (file[cursory+base]); + left = 0; + cursorx = t.length+1; + return true; + } + function term_vi_ee() { + return term_skipforward(/[^ ][ ]/, 1); + } + function term_vi_e() { + if (term_skipforward(/[a-zA-Z0-9_(){}<>][^a-zA-Z0-9_(){}<>]/, 1)) { + if (term_vi_flag('d') || term_vi_flag('c') || term_vi_flag('y')) cursorx++; + return true; + } + return false; + } + + function term_vi_v() { + cursorx = vselx - left; + if (cursorx < 0) { + left = 0; + cursorx = vselx; + } + cursory = vsely - base; + if (cursory < 0) { + base = 0; + cursory = vsely; + } + } + function term_vi_vv() { + term_vi_v(); + term_vi_line(); + } + + function term_select() { + if (vselm == 1) { + term_ex_motion = term_vi_v; + } else if (vselm == 2) { + term_ex_motion = term_vi_vv; + cursorx = 0; + left = 0; + } + } + function term_indent(y,amount) { + if (file[y] == undefined) { + file[y] = ''; + tags[y] = ''; + } + file[y] = _mxs(amount*4,32)+file[y]; + tags[y] = _mxs(amount*4,tagstyle)+tags[y]; + } + function term_unindent(y,amount) { + amount*=4; + while (amount > 0 && file[y].substr(0, 1) == ' ') { + file[y] = file[y].substr(1, file[y].length-1); + amount--; + } + } + + function term_operate() { + term_save_undo(); + + var fa = accum; + if (!fa) fa = 1; + var sx = cursorx+left; + var sy = cursory+base; + while (fa > 0) { + var t = file[base+cursory]; + if (t == undefined) break; + if (fa > 1 && ((cursorx+left) >= t.length)) { + cursory++; + cursorx = 0; + left = 0; + } + + term_ex_motion(); + fa--; + } + fa = accum; + if (!fa) fa = 1; + accum = 0; + var ex = cursorx+left; + var ey = cursory+base; + var i; + if (ey < sy) { + i = ey; + ey = sy; + sy = i; + } + if (ex < sx) { + i = ex; + ex = sx; + sx = i; + } + var t = file[ey]; + var g = tags[ey]; + var restore = false; + if (term_vi_flag('c')) term_vi_set('d'); + if (vselm != 2 && ey == sy && ex <= t.length) { + if (vselm) ex++; + if (ex != sx) { + if (term_vi_flag('F')) { + // styling + tags[ey] = (g.substr(0, sx)) + _mxs(ex-sx, tagstyle) + (g.substr(ex, g.length-ex)); + } else if (term_vi_flag('d') || term_vi_flag('y')) { + yank_buffer = _rtf(t.substr(sx, ex-sx), + g.substr(sx,ex-sx)); + } + if (term_vi_flag('d')) { + file[ey] = (t.substr(0, sx)) + (t.substr(ex, t.length-ex)); + tags[ey] = (g.substr(0, sx)) + (g.substr(ex, g.length-ex)); + if (lastreg != '_') { + registers[lastreg] = yank_buffer; + term_roll_yank(); + } + } else if (term_vi_flag('y')) { + registers[lastreg] = yank_buffer; + term_roll_yank(); + } + if (term_vi_flag('>')) { + term_indent(ey,fa); + } else if (term_vi_flag('<')) { + term_unindent(ey,fa); + } + } + } else if (vselm == 1) { + if (term_vi_flag('d') || term_vi_flag('y') || term_vi_flag('F')) { + yank_buffer = ''; + var al, bl; + for (i = sy; i <= ey; i++) { + t = file[i]; + g = tags[i]; + if (i == sy) { + if ((sy == vsely && sx == vselx) || (sy != vsely && sx != vselx)) { + al = sx; + } else { + al = ex; + } + bl = t.length; + } else if (i == ey) { + al = 0; + if ((sy == vsely && sx == vselx) || (sy != vsely && sx != vselx)) { + al = ex; + } else { + al = sx; + } + } else { + al = 0; + bl = t.length; + } + yank_buffer += _rtf(t.substr(al, bl-al), + g.substr(al,bl-al)); + if (sy != ey || vselm == 2) yank_buffer += "\n"; + if (term_vi_flag('d')) { + file[i] = t.substr(0, al) + t.substr(bl, t.length-bl); + tags[i] = g.substr(0, al) + g.substr(bl, g.length-bl); + } else if (term_vi_flag('F')) { + tags[i] = g.substr(0, al) + _mxs(bl-al, tagstyle) + + g.substr(bl, g.length-bl); + } + if (term_vi_flag('>')) { + term_indent(i,fa); + } else if (term_vi_flag('<')) { + term_unindent(i,fa); + } + } + if (lastreg != '_' && !term_vi_flag('F')) { + registers[lastreg] = yank_buffer; + term_roll_yank(); + } + } + } else { + if (term_vi_flag('F')) { + for (i = sy; i <= ey; i++) { + tags[i] = _mxs(file[i].length, tagstyle); + } + } else if (term_vi_flag('d')) { + yank_buffer = ''; + for (i = sy; i <= ey; i++) { + yank_buffer += term_delete(sy); + } + if (lastreg != '_') { + registers[lastreg] = yank_buffer; + term_roll_yank(); + } + } else if (term_vi_flag('y')) { + yank_buffer = ''; + for (i = sy; i <= ey; i++) { + yank_buffer += _rtfl(sy) + "\n"; + } + registers[lastreg] = yank_buffer; + term_roll_yank(); + } else if (term_vi_flag('>')) { + for (i = sy; i <= ey; i++) { + term_indent(i,fa); + } + } else if (term_vi_flag('<')) { + for (i = sy; i <= ey; i++) { + term_unindent(i,fa); + } + } + } + + if (term_vi_flag('d')) { + term_vi_unset('d'); + lastcommand = 'd'; + lastmotion = term_ex_motion; + restore = true; + } + if (term_vi_flag('y')) { + term_vi_unset('y'); + restore = true; + } + if (term_vi_flag('F')) { + term_vi_unset('F'); + restore = true; + } + lastaccum = accum; + if (restore) { + cursorx = sx - left; + if (cursorx < 0) { + cursorx = sx; + left = 0; + } + cursory = sy - base; + if (cursory < 0) { + cursory = sy; + base = 0; + } + term_scrollto(); + } + if (term_vi_flag('c')) { + term_vi_unset('c'); + term_setmode(1); + } + accum = 0; + } + + function term_save_undo_line() { + if ((base+cursory) != undoy) { + undoy = base+cursory; + undoline = _rtfl(undoy); + } + } + function term_save_undo() { + undoset = term_freeze(); + } + + function term_delete(i) { + if (i > file.length) return ''; + var j; + var z = file[i]; + var g = tags[i]; + for (j = i+1; j < file.length; j++) { + file[j-1] = file[j]; + tags[j-1] = tags[j]; + } + file=_pop(file); + return _rtf(z,g) + "\n"; + } + + function term_skipreverse2(re, fuzz) { + var y = cursory+base; + var x = (cursorx+left)+(1+fuzz); + for (;;) { + var t = file[y]; + if (t == undefined) { + // beep + cursory = y - base; + return false; + } + if (fuzz) t += " "; else t = " " + t; + t = (t.substr(x, t.length-x)); + var aa = re.exec(t); + if (!aa) { + y--; + x = 0; + } else { + x += _rel(re,t,aa).length; + cursorx = x - left; + cursory = y - base; + return true; + } + } + return false; + } + function term_skipforward(re, fuzz) { + var y = cursory+base; + var x = (cursorx+left)+(1+fuzz); + for (;;) { + var t = file[y]; + if (t == undefined) { + // beep + cursory = y - base; + return false; + } + if (fuzz) t += " "; else t = " " + t; + t = (t.substr(x, t.length-x)); + var aa = re.exec(t); + if (!aa) { + y++; + x = 0; + } else { + x += _rel(re,t,aa).length; + cursorx = x - left; + cursory = y - base; + return true; + } + } + return false; + } + function term_skipbackward(re) { + var y = cursory+base; + var x = (cursorx+left)+1; + for (;;) { + var t = file[y]; + if (t == undefined) { + // beep + cursory = 0; + cursorx = 0; + return false; + } + t = " " + t.substr(0, x-1); + if (t == '') { + y--; + t = file[y]; + if (t == undefined) continue; + x = (t.length); + continue; + } + var aa = re.exec(t); + if (!aa) { + left = 0; + cursorx = 0; + cursory = y - base; + return true; + } else { + x = _rel(re,t,aa).length; + cursorx = x - left; + cursory = y - base; + return true; + } + } + return false; + } + + function term_search(s, top, start, bottom) { + var re = new RegExp(s.substr(1)); + var i; + if (s.substr(0, 1) == '/') { + statustext=''; + re.lastIndex = cursorx+left+1; + for (i = start; i < bottom; i++) { + var t = (file[i]); + if (i == start) { + t = t.substr(cursorx+left+1, t.length-(cursorx+left+1)); + } + aa = re.exec(t); + if (!aa) continue; + var tx = _rel(re,t,aa).length; + if (i == start) { + tx += cursorx+left+1; + } + left = 0; + base = 0; + cursorx = tx; + cursory = i; + term_scrollto(); + return true; + } + statustext = 'search hit BOTTOM, continuing at TOP'; + for (i = top; i <= start; i++) { + var aa = re.exec(file[i]); + if (!aa) continue; + left = 0; + base = 0; + cursorx = _rel(re,file[i],aa).length; + cursory = i; + term_scrollto(); + return true; + } + statustext = 'Pattern not found: ' + s.substr(1); + } else { + statustext=''; + for (i = start; i >= top; i--) { + var t = file[i]; + if (t == undefined) continue; + var tail = 0; + if (i == start) { + tail = (cursorx+left); + } else { + tail = t.length; + } + var right = tail; + while (tail > 0) { + tail--; + var xj = t.substr(tail, right-tail); + var aa = re.exec(xj); + if (!aa) continue; + + var tx = tail+_rel(re,xj,aa).length; + left = 0; + base = 0; + cursorx = tx; + cursory = i; + term_scrollto(); + return true; + } + } + statustext = 'search hit TOP, continuing at BOTTOM'; + for (i = bottom; i >= start; i--) { + var t = file[i]; + if (t == undefined) continue; + var tail = t.length; + while (tail > 0) { + tail--; + var xj = t.substr(tail, t.length-tail); + var aa = re.exec(xj); + if (!aa) continue; + + cursorx = tail+_rel(re,xj,aa).length; + cursory = i; + left = 0; + base = 0; + term_scrollto(); + return true; + } + } + statustext = 'Pattern not found: ' + s.substr(1); + } + return false; + } + function term_rsearch(s, top, start, bottom) { + var cx = s.substr(0, 1); + cx = (cx == '/') ? '?' : "/"; + return term_search(cx+s.substr(1,s.length-1), top, start, bottom); + } + + function _pop(q) { + var a = new Array(); + var i; + for (i = 0; i < q.length-1; i++) { + a[i] = q[i]; + } + return a; + } + function _addr(q) { + if (q == '.') { + return cursory+base; + } + if (q == '$') { + return file.length-1; + } + if (q.substr(0, 1) == "'") { + return marks[ q.substr(1, 1) ]; + } + if (q == "\\/" || q == "\\&") { + var a=cursory; + var b=base; + term_search(lastsearch, 0, cursory+base, file.length); + var c=cursory+base; + cursory=a; + base=b; + return c; + } + if (q == "\\?") { + var a=cursory; + var b=base; + term_rsearch(lastsearch, 0, cursory+base, file.length); + var c=cursory+base; + cursory=a; + base=b; + return c; + } + if (q.substr(0, 1) == "/" || q.substr(0,1) == "?") { + var a=cursory; + var b=base; + term_search(q, 0, cursory+base, file.length); + var c=cursory+base; + cursory=a; + base=b; + return c; + } + q=parseInt(q)-1; + if (q >= file.length-1) q=file.length-1; + if (q < 0) q=0; + return q; + } + function term_command(s) { + var top, start,bottom; + if (s && s.length > 0 && s.substr(0,1) == ':') { + s = s.substr(1, s.length-1); + top = cursory+base; + start = top; + bottom = top; + } else { + top = 0; + start = cursory+base; + bottom = file.length; + } + + // okay, this is kind of tricky + var i; + var tok = ''; + var tc = 0; + var ng = new Array(); + var lastre = undefined; + var nf = false; + /// xxx todo implement ! with nf + for (i = 0; i < s.length; i++) { + var c = s.substr(i, 1); + if ((tc==0 || tc==1) && "0123456789".indexOf(c) > -1) { + tc = 1; + tok = ''+tok+''+c; + continue; + + } else if (tc == 1) { + tc = 0; + ng[ng.length] = parseInt(tok)-1; + tok = ''; + } + + if (c == '%') { + top = 0; + bottom = file.length; + start = cursory+base; + } else if (c == '!') { + nf = !nf; + } else if (c == ',') { + // do nothing + while (ng.length > 2) { + ng=_pop(ng); + } + } else if (c == ';') { + start = ng[ng.length-1]; + ng = _pop(ng); + cursory=start; + base=0; + ng = _pop(ng); + } else if (c == '$') { + ng[ng.length] = file.length; + } else if (c == '.') { + ng[ng.length] = cursory+base; + } else if (c == "'") { + // mark + ng[ng.length] = marks[s.substr(i+1,1)]; + i++; + + } else if (c == "\\") { + i++; + c = s.substr(i, 1); + var qq; + if (c == '?') { + qq=term_rsearch; + } else { + qq=term_search; + } + if (ng.length == 1) { + top = ng[0]; + ng = _pop(ng); + } else if (ng.length >= 2) { + top = ng[ng.length-2]; + bottom = ng[ng.length-1]; + ng = _pop(_pop(ng)); + } + if (!qq(lastsearch,top,start,bottom)) { + return; + } + ng[ng.length] = cursory+base; + } else if (c == "/" || c == "?") { + var j = i; + for (i++; i < s.length; i++) { + var tc = s.substr(i, 1); + if (tc == "\\") i++; + else if (tc == c) break; + } + lastre = s.substr(j, i-j); + if (ng.length == 1) { + top = ng[0]; + ng = _pop(ng); + } else if (ng.length >= 2) { + top = ng[ng.length-2]; + bottom = ng[ng.length-1]; + ng = _pop(_pop(ng)); + } + if (!term_search(lastre,top,start,bottom)) { + return; + } + ng[ng.length] = cursory+base; + } else if (c == " " || c == "\t") { + continue; + } else { + break; + } + } + if (tc == 1) { + ng[ng.length] = parseInt(tok)-1; + tok = ''; + i++; + } + if (ng.length == 1) { + top = ng[0]; + bottom = ng[0]; + } else if (ng.length >= 2) { + top = ng[ng.length-2]; + bottom = ng[ng.length-1]; + } + if (lastre != undefined) { + lastsearch = lastre; + registers["/"] = lastsearch.substr(1, lastsearch.length-1); + } + + var cmd2 = s.substr(i,2); + var cmd = s.substr(i, 1); + + if (cmd2 == 'wq' || cmd == 'x') { + editor_disable(true); + } else if (cmd == '=') { + statustext = '' + bottom; + return; + } else if (cmd2 == 'ha') { + window.print(); + return; + } else if (cmd == 'w') { + var zx = term_freeze(); + if (term._formelement) term._formelement.value=zx; + statustext = '"/tmp/mess4XbCXM" ' + file.length + 'L, ' + + zx.length + 'C written'; + if (onSave) { + onSave(); + } + + } else if (!emacsen && s.substr(i,(s.length-i)) == 'emacs') { + statustext = 'EMACS mode enabled. Press M-x vi to use vi mode'; + emacsen = true; + mode = 1; + + } else if (emacsen && cmd2 == 'vi') { + statustext = 'VI mode enabled. Press ESC :emacs to use EMACS mode'; + mode = 0; + emacsen = false; + + } else if (cmd == 'e' && term._formelement) { + var zx = term_freeze(); + if (cmd2 != 'e!') { + if (cmd2 == 'e?') { + if (!confirm("Your changes will be lost\nAre you sure?")) { + return; + } + } else if (term._formelement.value != zx) { + statustext = 'No write since last change (use ! to override)'; + return; + } + } + term_thaw(term._formelement.value); + } else if (cmd == 'f') { + var zx = term_freeze(); + statustext = '"/tmp/mess4XbCXM"'; + if (term._formelement.value != zx) { + statustext += ' [Modified]'; + } + statustext += ' line ' + (cursory+base+1) + ' of ' + + file.length + ' col ' + (cursorx+left+1); + + } else if (cmd == 'h' || s.substr(i,5) == 'about') { + statustext = "jsvi \xa9 2006 Internet Connection, Inc; 2013 Jakub Jankiewicz"; + + } else if (s.substr(i,4) == 'kwak') { + term.style.backgroundImage = 'url(ducky.jpg)'; + statustext = 'kwak kwak kwak...'; + } else if (s.substr(i,3) == 'moo') { + statustext = 'This editor does not have Super Cow Powers'; + + } else if (cmd == 'b') { + // only one buffer + + } else if (cmd == 'n' || cmd == 'N') { + statustext = 'There is only one file to edit'; + + } else if (cmd == 'q') { + var zx = term_freeze(); + if (cmd2 != 'q!') { + if (term._formelement.value != zx) { + if (cmd2 == 'q?') { + if (confirm("Your changes will be lost\nAre you sure?")) { + editor_disable(false); + return; + } else { + return; + } + } + statustext = 'No write since last change (use ! to override)'; + return; + } + } + editor_disable(false); + if (onExit) { + onExit(); + } + } else if (cmd == 'd') { + // delete lines + yank_buffer = term_delete(top); + while (top < bottom) { + yank_buffer += term_delete(top); + bottom--; + } + if (lastreg != '_') { + registers[lastreg] = yank_buffer; + term_roll_yank(); + } + term_scrollto(); + } else if (cmd == 'u') { + var z = term_freeze(); + term_thaw(undoset); + undoset = z; + + } else if (cmd == 'y') { + yank_buffer = ''; + for (i = top; i < bottom; i++) { + yank_buffer += (file[i])+"\n"; + } + registers[lastreg] = yank_buffer; + term_roll_yank(); + + } else if (cmd == 'F') { + // styling! + var y; + var tadd = 0; + var tsub = 0; + var tset = undefined; + var otc = tagstyle; + var tg = '='; + for (y = i+1; y < s.length; y++) { + var cy = s.substr(y, 1); + if (cy == '=' || cy == '+' || cy == '-' || cy == '!') { + tg = cy; + } else { + var fl = 0; + if (cy == 'b') { + fl = 1; + } else if (cy == 'i') { + fl = 16; + } else if (cy == 'u') { + fl = 2; + } else if (cy == 'o') { + fl = 4; + } else { + statustext = 'Unrecognized formatting specifier: ' + cy; + return; + } + if (tg == '!') { + if (tset != undefined) tagstyle = tset; + tagstyle = tagstyle ^ fl; + } else if (tg == '=') { + if (tset == undefined) tset = 0; + tset = tset + fl; + } else if (tg == '+') { + tadd |= fl; + } else if (tg == '-') { + tsub |= fl; + } + } + } + if (tset == undefined) { + tagstyle = ((tagstyle | tadd) | tsub) ^ tsub; + } else { + tagstyle = tset; + } + // okay, we've set tagstyle + if (i != 0) { + // if this happened, then we were given a range(!) + for (y = top; y <= bottom; y++) { + if (file[y] == undefined) continue; + tags[y] = _mxs(file[y].length, tagstyle); + } + tagstyle = otc; + + } else if (vselm) { + term_select(); + term_vi_unset('d'); + term_vi_unset('y'); + term_vi_unset('c'); + term_vi_set('F'); + term_operate(); + tagstyle = otc; + } + + + } else if (cmd == 'g' || cmd == 'v') { + // okay then + var y; + var ng = s.substr(i+1,(s.length-i)-1); + if (cmd == 'v') ng = '!' + ng; + for (y = 0; y < file.length; y++) { + base = 0; + cursory = y; + term_command(ng); + } + + } else if (cmd == 't' || cmd2 == 'co') { + // copy- need address + var t = s.substr(i, s.length-i).replace(/^[a-z]*[ ]*/); + var x; + yank_buffer = ""; + for (x = top; x <= bottom; x++) { + if (file[x] != undefined) { + yank_buffer += (file[x]) + "\n"; + } + } + var a = cursory; + var b = base; + base = 0; + cursorx = 0; + left = 0; + cursory = _addr(t); + if (cursory < 0) cursory = 0; + term_paste(false, yank_buffer); + cursory = a; + base = b; + + } else if (cmd == 'm') { + // move- need address + i++; + if (s.substr(i, 3) == 'ove') { + i += 3; + } + var t = s.substr(i, s.length-i); + yank_buffer = term_delete(top); + while (top < bottom) { + yank_buffer += term_delete(top); + bottom--; + } + var a = cursory; + var b = base; + base = 0; + cursorx = 0; + left = 0; + cursory = _addr(t); + if (cursory < 0) cursory = 0; + term_paste(false, yank_buffer); + cursory = a; + base = b; + + } else if (cmd == 's') { + // substitute + i++; + // extract regex + var q = s.substr(i,1); + while (q == ' ') { + i++; + q = s.substr(i,1); + } + var sep = s.substr(i,1); + i++; + + var lr, ls, lf; + if (sep == '') { + if (!lastsearch || !lastsubst) { + statustext = 'No previous substitute regular expression'; + return; + } + lr = lastsearch.substr(1, lastsearch.length-1); + ls = lastsubst; + lf = lastflags; + } else { + var jj = i; + var zj; + for (; i < s.length; i++) { + zj = s.substr(i,1); + if (zj == "\\") { + i++; + } else if (zj == sep) { + break; + } + } + lastsearch = "/" + s.substr(jj, i-jj); + registers["/"] = lastsearch.substr(1, lastsearch.length-1); + i++; // sep + jj=i; + for (; i < s.length; i++) { + zj = s.substr(i,1); + if (zj == "\\") { + i++; + } else if (zj == sep) { + break; + } + } + lastsubst = s.substr(jj, i-jj); + lastflags = ''; + i++; + var count = -1; + for (; i < s.length; i++) { + zj = s.substr(i,1); + if (zj == 'i' || zj == 'g') { + lastflags += zj; + } else { + count = parseInt(s.substr(i,s.length-i)); + if (count > 0) { + break; + } else { + statustext = "Trailing characters"; + return; + } + } + } + var re = new RegExp; + re.compile(lastsearch.substr(1, lastsearch.length-1), + (lastflags.indexOf('i') > -1) ? 'i' : ''); + var hit = false; + for (i = top; i < bottom; i++) { + var t = file[i]; + var g = tags[i]; + var aa; + var st = t; + aa = re.exec(t); + if (!aa || aa.length == 0) continue; + file[i] = ''; + tags[i] = ''; + // okay got a hit, do this vi style + for (;;) { + t = _rer(re,st,aa); + var lt = _rel(re,st,aa); + var lg = _resubst(lastsubst,aa); + file[i] += lt + lg; + tags[i] += g.substr(0, lt.length) + _zeros(lg.length); + + if (lastflags.indexOf('g') > -1) { + st = t; + aa = re.exec(t); + if (aa && aa.length > 0) continue; + } + file[i] += t; + tags[i] += g.substr(g.length - t.length, t.length); + break; + } + hit = true; + if (count > -1) { + count--; + if (count == 0) break; + } + } + if (!hit) { + statustext = 'Pattern not found: ' + lastsearch.substr(1, lastsearch.length-1); + } + } + + } else if (cmd == '' && ng.length > 0) { + base = 0; + cursory = top; + } else { + statustext = "Not an editor command: " + s.substr(i,s.length-i); + } + + } + function _resubst(s, aa) { + var i; + var out = ''; + for (i = 0; i < s.length; i++) { + var zq = s.substr(i, 1); + if (zq == "\\") { + zq = s.substr(i, 2); + if (zq == "\\1") { + zq = aa[1]; + } else if (zq == "\\2") { + zq = aa[2]; + } else if (zq == "\\3") { + zq = aa[3]; + } else if (zq == "\\4") { + zq = aa[4]; + } else if (zq == "\\5") { + zq = aa[5]; + } else if (zq == "\\6") { + zq = aa[6]; + } else if (zq == "\\7") { + zq = aa[7]; + } else if (zq == "\\8") { + zq = aa[8]; + } else if (zq == "\\9") { + zq = aa[9]; + } else { + zq = s.substr(i+1, 1); + } + i++; + } else if (zq == "&") { + zq = aa[0]; + } + out += zq; + } + return out; + } + function _calcy(zx, x, g) { + if (zx && cursor._lasty != cursory) { + cursor._lasty = cursory; + var nh = 0; + while (zx && zx != document.body) { + nh += zx.offsetTop; + zx = zx.offsetParent; + } + cursor.style.top = nh + 'px'; + } + + var z = x.substr(cursorx, 1); + var q = g.substr(cursorx, 1).charCodeAt(0); + if (cursorx >= x.length || z == undefined || z == "\240" || z == '') + z = ' '; + + if (cursor._lastch != z || cursor._lastgh != q) { + if (z == ' ') { + z = "\240"; + q = 0; + } + while (cursor.firstChild) + cursor.removeChild(cursor.firstChild); + cursor.appendChild(document.createTextNode(z)); + + cursor._lastch = z; + cursor._lastgh = q; + + if (q & 1) { + cursor.style.fontWeight = 'bold'; + } else { + cursor.style.fontWeight = 'normal'; + } + if (q & 2) { + cursor.style.textDecoration = 'underline'; + } else { + cursor.style.textDecoration = 'none'; + } + if (q & 16) { + cursor.style.fontStyle = 'italic'; + } else { + cursor.style.fontStyle = 'normal'; + } + } + } + function term_calcy() { + // fixup character inside... burrr + var xx = file[cursory+base]; + var xg = tags[cursory+base]; + var zleft = left; + if (cursory == (term_rows-1)) { + xx = command + " "; + zleft = commandleft; + xg = _mxs(xx.length+1,0); + } + var gg = term.childNodes; + if (gg.length > cursory) { + gg = term.childNodes[cursory]; + } else { + gg = undefined; + } + _calcy(gg, xx.substr(zleft, xx.length-zleft), xg.substr(zleft, xg.length-zleft)); + } + function term_calcx() { + if (cursorx != cursor._lastx) { + cursor.style.left = (cursorx * (term_cur_width)) + 'px'; + cursor._lastx = cursorx; + term_calcy(); + } + } + function term_scrollto() { + + var h = term_rows; + var w = term_cols; + var x = cursorx+left; + var y = cursory+base; + var t = file[y]; + + if (command == '') h--; + + var ex = parseInt((w/8)); + if (ex < 4) ex = 4; + var ey = parseInt((h/8)); + if (ey < 4) ey = 4; + + while (t == undefined && y > 0) { + y--; + t = file[y]; + } + if (t == undefined) t = ''; + if (x >= t.length) { + if (mode) x = t.length; + else x = t.length-1; + } + if (x < 0) x = 0; + if (y < 0) y = 0; + + if (x < left) { + left = (x - ex); + cursorx = ex; + if (left < 0) { + left = 0; + cursorx = 0; + } + } else if (x >= left+w) { + left = (x - w) + ex; + cursorx = w - ex; + } else { + cursorx = x - left; + } + if (y < base) { + base = (y - ey); + cursory = ey; + if (base < 0) { + base = 0; + cursory = 0; + } + } else if (y >= base+h) { + base = ((y - h) + ey); + cursory = (h - ey); + } else { + cursory = y - base; + } + term_calcx(); + term_calcy(); + } + + function term_insert(y, str) { + var z = file.length; + file[z] = ''; + tags[z] = ''; + var i; + for (i = z; i > y; i--) { + file[i] = (file[i-1]); + tags[i] = (tags[i-1]); + } + file[y] = ''; + tags[y] = ''; + } + function term_paste(after, ign) { + if (ign != undefined) { + yank_buffer = ign; + } else { + registers['.'] = lastinsert; + registers[':'] = oldcommand; + registers['%'] = "/tmp/mess4XbCXM"; + registers['#'] = ""; + yank_buffer = registers[lastreg]; + } + if (yank_buffer == undefined) return false; + if (yank_buffer.indexOf("\n") > -1) { + // insert after this line + if (after) cursory++; + var nq = yank_buffer.split("\n"); + var nj; + for (nj = 0; nj < nq.length; nj++) { + if (nj == (nq.length-1)) { + if (nq[nj] == undefined) break; + if (nq[nj] == '') break; + // handled specially + var aa = _hra(nq[nj]); + file[cursory+base+nj] = aa[0] + (file[cursory+base+nj]); + tags[cursory+base+nj] = aa[1] + (tags[cursory+base+nj]); + break; + } + term_insert(cursory+base+nj); + var aa = _hra(nq[nj]); + file[cursory+base+nj] = aa[0]; + tags[cursory+base+nj] = aa[1]; + } + } else { + var t = (file[cursory+base]); + var g = (tags[cursory+base]); + var aa = _hra(yank_buffer); + if (after) cursorx++; + file[cursory+base] = t.substr(0, cursorx+left) + + aa[0] + t.substr(cursorx+left, t.length-(cursorx+left)); + tags[cursory+base] = g.substr(0, cursorx+left) + + aa[1] + g.substr(cursorx+left, g.length-(cursorx+left)); + } + return true; + } + function term_keyfix(e) { + if (!e) e = window.event; + + var ch = e.keyCode; + if (!ch) ch = e.which; + + if (e.DOM_VK_UP) { + if (e.DOM_VK_UP == ch) ch = 57373; + else if (e.DOM_VK_DOWN == ch) ch = 57374; + else if (e.DOM_VK_LEFT == ch) ch = 57375; + else if (e.DOM_VK_RIGHT == ch) ch = 57376; + } + + if (ch == 8 || ch == 9 || ch == 37 || ch == 39 + || ch == 38 || ch == 40 || ch == 127 + || ch == 33 || ch == 34 || ch == 36 + || ch == 35 || ch == 45 || ch == 46 + || ch == 57373 + || ch == 57374 + || ch == 57375 + || ch == 57376) { + if (e.preventDefault) e.preventDefault(); + if (e.stopPropagation) e.stopPropagation(); + term_keypress_inner(e, true); + e.cancelBubble=true; + return false; + } else { + e.cancelBubble=false; + return true; + } + + } + function term_keypress(e) { + if (!e) e = window.event; + if (e.preventDefault) e.preventDefault(); + if (e.stopPropagation) e.stopPropagation(); + if (suggest._visible) { + suggest.style.display = 'none'; + suggest._visible = false; + } + e.cancelBubble=true; + term_keypress_inner(e, false); + return false; + } + + function term_keyup(e) { + if (fakemode || mode === 0) { + vselm = 0; + statustext = ''; + mode = 0; + } else { + if (e.keyCode == 27) { // escape + vselm = 0; + statustext = ''; + if (command != '') { + cursorx = savex; + cursory = savey; + } else { + if (accum > 1) { + var i, j; + j = lastreg; + lastreg = '.'; + for (i = 1; i < accum; i++) { + term_paste(false); + } + lastreg = j; + accum = 0; + } + cursorx--; + term_scrollto(); + } + mode = emacsen ? 1 : 0; + command = ''; + commandleft = 0; + } + } + term_redraw(); + _update_backing(); + } + + function term_keypress_inner(e, synth) { + var k = e.which; + var kc; + if (e.charCode) { + if (e.charCode == 27 || e.charCode == 13 || e.charCode == 10 + || e.charCode == 8 + || e.charCode == 9) { + k = e.charCode; + kc = String.fromCharCode(e.charCode); + } else if (e.charCode == 63232) { + k = 57373; + } else if (e.charCode == 63233) { + k = 57374; + } else if (e.charCode == 63234) { + k = 57375; + } else if (e.charCode == 63235) { + k = 57376; + } else { + if (e.charCode == 191) return; // wtf? + kc = String.fromCharCode(e.charCode); + k = 0; + } + } else if (e.keyCode) { + k = e.keyCode; + if (e.DOM_VK_UP) { + if (e.DOM_VK_UP == k) k = 57373; + else if (e.DOM_VK_DOWN == k) k = 57374; + else if (e.DOM_VK_LEFT == k) k = 57375; + else if (e.DOM_VK_RIGHT == k) k = 57376; + } else { + kc = String.fromCharCode(e.keyCode); + } + if (k == 191) return; // unicode i think + } else { + if (!k || k == 191) return; + kc = String.fromCharCode(k); + var i; + } + var mod; + if (e.modifiers) mod = e.modifiers; else mod = 0; + var ctrl = e.ctrlKey | (mod & 2); + var shift = e.shiftKey | (mod & 4); + var meta = e.altKey | e.metaKey | (mod & 1); + var lk = lastkey; + lastkey = kc; + + if (kc == undefined) kc = ''; + if (!emacsen && mode == 0) { + if (kc == 'U') { + if ((base+cursory) == undoy) { + var aa = _hra(undoline); + file[undoy] = aa[0]; + tags[undoy] = aa[1]; + } + } else if (kc == 'u' && undoset) { + var z = term_freeze(); + term_thaw(undoset); + undoset = z; + } + + if (kc == 'u' || kc == 'U') { + term_scrollto(); + term_redraw(); + lastkey = undefined; + return; + } + } + + fakemode = false; + + // emacsen + if (emacsen && meta && (kc == 'x' || kc == 'X')) { + kc = ':' + fakemode = true; + lk = undefined; + lastkey = undefined; + } else if (ctrl && (kc == 't' || kc == 'T')) { + kc = '?' + fakemode = true; + lk = undefined; + lastkey = undefined; + } else if (ctrl && (kc == 's' || kc == 'S')) { + kc = '/' + fakemode = true; + lk = undefined; + lastkey = undefined; + } else if (ctrl && (kc == 'k' || kc == 'K')) { + term_command(':d'); + lastkey = undefined; + return; + } else if (emacsen && meta && (kc == 'd' || kc == 'D')) { + kc = 'D'; + fakemode = true; + lk = undefined; + lastkey = undefined; + } else if (ctrl && (kc == 'a' || kc == 'A')) { + kc = '0'; + fakemode = true; + lk = undefined; + lastkey = undefined; + } else if (ctrl && (kc == 'd' || kc == 'D')) { + if (emacsen) { + kc = 'x'; + } else { + // err... + if (!accum) accum=term_rows+1; + cursory += parseInt(accum/2)+2; + accum=0; + term_scrollto(); + term_redraw(); + lk = undefined; + lastkey = undefined; + return; + } + fakemode = true; + lk = undefined; + lastkey = undefined; + } else if (ctrl && (kc == 'g' || kc == 'G')) { + term_command(':f'); + lastkey = undefined; + return; + } else if (ctrl && (kc == 'e' || kc == 'E')) { + if (emacsen) { + kc = '$'; + } else { + if (!accum) accum = 1; + cursory += accum; + accum = 0; + term_scrollto(); + term_redraw(); + lk = undefined; + lastkey = undefined; + return; + } + fakemode = true; + lk = undefined; + lastkey = undefined; + } else if (emacsen && meta && (kc == 'b' || kc == 'B')) { + kc = 'b'; + fakemode = true; + lk = undefined; + lastkey = undefined; + } else if (emacsen && meta && (kc == 'l' || kc == 'L')) { + kc = 'e'; + fakemode = true; + lk = undefined; + lastkey = undefined; + } else if (emacsen && meta && (kc == 'f' || kc == 'F')) { + kc = 'w'; + fakemode = true; + lk = undefined; + lastkey = undefined; + } else if (ctrl && (kc == 'f' || kc == 'F')) { + if (emacsen) { + kc = 'l'; + } else { + // vi pagedown + if (!accum) accum = 0; + cursory += (term_rows*accum); + accum = 0; + term_scrollto(); + term_redraw(); + lk = undefined; + lastkey = undefined; + return; + } + fakemode = true; + lk = undefined; + lastkey = undefined; + } else if (ctrl && (kc == 'y' || kc == 'Y')) { + if (emacsen) { + term_command(':y'); + lastkey = undefined; + return; + } else { + if (!accum) accum = 1; + cursory -= accum; + accum = 0; + term_scrollto(); + term_redraw(); + lk = undefined; + lastkey = undefined; + return; + } + } else if (ctrl && (kc == 'l' || kc == 'L')) { + term_redraw(); + lk = undefined; + lastkey = undefined; + return; + } else if (ctrl && (kc == 'u' || kc == 'U')) { + if (emacsen) { + // emacs undo + kc = 'u'; + } else { + // vi pageup + if (!accum) accum=term_rows+1; + cursory -= parseInt(accum/2)+2; + accum=0; + term_scrollto(); + term_redraw(); + lk = undefined; + lastkey = undefined; + return; + } + fakemode = true; + lk = undefined; + lastkey = undefined; + } else if (ctrl && (kc == 'w' || kc == 'W')) { + // vim uses this for window control; this is actually useful + fakemode = true; + term_vi_set('d'); + kc = 'b'; + lk = undefined; + lastkey = undefined; + + } else if (ctrl && (kc == 'b' || kc == 'B')) { + if (emacsen) { + kc = 'h'; + } else { + // vi pageup + if (!accum) accum=1; + cursory -= (term_rows*accum); + accum=0; + term_scrollto(); + term_redraw(); + lk = undefined; + lastkey = undefined; + return; + } + fakemode = true; + lk = undefined; + lastkey = undefined; + } + if (k >= 57373 && k <= 57376 && !synth) { + // bail + return; + } else if (!synth && (k < 57373 || k > 57376)) { + // no touching... + } else if (k == 31 || k == 40 || k == 57374) { // down + if (!synth) return; + fakemode = true; + kc = 'j'; + lk = undefined; + lastkey = undefined; + } else if (k == 30 || k == 38 || k == 57373) { // up + if (!synth) return; + fakemode = true; + kc = 'k'; + lk = undefined; + lastkey = undefined; + } else if (k == 28 || k == 37 || k == 57375) { // left + if (!synth) return; + fakemode = true; + kc = ctrl ? 'B' : 'h'; + lk = undefined; + lastkey = undefined; + } else if (k == 29 || k == 39 || k == 57376) { // right + if (!synth) return; + fakemode = true; + kc = ctrl ? 'W' : 'l'; + lk = undefined; + lastkey = undefined; + } else if (k == 33) { // pageup + cursory -= term_rows; + term_scrollto(); + term_redraw(); + lk = undefined; + lastkey = undefined; + return; + } else if (k == 34) { // pagedown + cursory += term_rows; + term_scrollto(); + term_redraw(); + lk = undefined; + lastkey = undefined; + return; + } else if (k == 36) { // home + if (ctrl) { + lk = 'g'; + kc = 'g'; + } else { + kc = '0'; + } + fakemode = true; + lk = undefined; + lastkey = undefined; + accum = 0; + } else if (k == 35) { // end + fakemode = true; + accum = 0; + if (ctrl) { + kc = 'G'; + } else { + kc = '$'; + } + lk = undefined; + lastkey = undefined; + } else if (k == 45) { // insert + fakemode = true; + if (mode == 1) { + mode = 2; + } else if (mode == 2) { + mode = 1; + } else { + term_setmode(1); + } + lk = undefined; + lastkey = undefined; + term_redraw(); + return; + } else if (k == 46) { // delete + fakemode = true; + kc = 'x'; + lk = undefined; + lastkey = undefined; + } else if (synth && k != 8) { + return; + } + + term_save_undo_line(); + + if (fakemode || mode == 0) { + if (!fakemode && ctrl) return; + if (lk == 'F' || lk == 'T' || lk == 'f' || lk == 't') { + vi_ft_reg = kc; + if (lk == 'F') { + term_ex_motion = term_vi_ff; + } else if (lk == 'T') { + term_ex_motion = term_vi_tt; + } else if (lk == 'f') { + term_ex_motion = term_vi_f; + } else if (lk == 't') { + term_ex_motion = term_vi_t; + } + term_operate(); + lastkey = undefined; + term_scrollto(); + term_redraw(); + _update_backing(); + return; + } else if (lk == 'm') { + // set mark + marks[kc] = cursory+base; + lastkey = undefined; + accum = 0; + return; + + } else if (lk == "'") { + term_ex_motion = function() { + cursory = marks[kc]; + base = 0; + term_scrollto(); + term_redraw(); + return true; + }; + if (!accum) accum = 1; + term_operate(); + return; + + } else if (lk == '"') { + // set last register + lastreg = kc; + lastkey = undefined; + accum = 0; + return; + + } else if (lk == 'r') { + var p = cursorx + left; + var t = (file[cursory+base]); + if (!accum) accum = 1; + var ng = ''; + var i; + for (i = 0; i < accum; i++) ng += kc; + term_save_undo(); + file[cursory+base] = (t.substr(0, p)) + + (ng) + (t.substr(p+accum, t.length-(p+accum))); + // tags remains unchanged (same length) + accum = 0; + lastkey = undefined; + term_scrollto(); + term_redraw(); + _update_backing(); + return; + } + if (kc == ':' || kc == '/' || kc == '?') { + command = kc; + commandleft = 0; + savex = cursorx; + savey = cursory; + cursorx = 1; + cursory = term_rows-1; + mode=1; + term_redraw(); + term_calcx(); + return; + } else if (kc == "'") { + // jump to mark (ick) + } else if (kc == '"') { + // do nothing for now + + } else if (kc == '.') { + term_vi_set(lastcommand); + term_ex_motion = lastmotion; + term_operate(); + + } else if (kc == '$') { + term_ex_motion = term_vi_eol; + term_operate(); + + } else if (kc == '%') { + if (accum <= 0) { + term_ex_motion = term_vi_bounce; + term_operate(); + } else if (file.length > 0) { + base = 0; + cursorx = 0; + left = 0; + cursory = parseInt(accum * (term_rows / (file.length+1.0))); + accum = 0; + } + + } else if (kc == '0') { + if (accum) { + accum *= 10; + } else { + cursorx = 0; + left = 0; + } + } else if (kc == '1') { + accum *= 10; accum++; + } else if (kc == '2') { + accum *= 10; accum += 2; + } else if (kc == '3') { + accum *= 10; accum += 3; + } else if (kc == '4') { + accum *= 10; accum += 4; + } else if (kc == '5') { + accum *= 10; accum += 5; + } else if (kc == '6') { + accum *= 10; accum += 6; + } else if (kc == '7') { + accum *= 10; accum += 7; + } else if (kc == '8') { + accum *= 10; accum += 8; + } else if (kc == '9') { + accum *= 10; accum += 9; + } else if (kc == 'A') { + var t = (file[cursory+base]); + cursorx = t.length; // will be fixed up... + term_setmode(1); + term_save_undo(); + } else if (kc == 'a') { + term_setmode(1); + cursorx++; + term_save_undo(); + } else if (kc == 'B') { + term_ex_motion = term_vi_bb; + term_operate(); + } else if (kc == 'b') { + term_ex_motion = term_vi_b; + term_operate(); + } else if (kc == 'c') { + if (term_vi_flag('c')) { + term_vi_set('d'); + term_vi_unset('c'); + savex = cursorx+left; + savey = cursory+base; + cursorx = 0; left = 0; + term_ex_motion = term_vi_line; + term_operate(); + cursorx = savex-left; + cursory = savey-base; + term_setmode(1); + } else { + term_vi_set('c'); + } + } else if (kc == 'C') { + term_ex_motion = term_vi_eol; + term_vi_set('d'); + term_operate(); + term_setmode(1); + } else if (kc == 'D') { + term_ex_motion = term_vi_eol; + term_vi_set('d'); + term_operate(); + } else if (kc == 'd') { + if (vselm) { + term_vi_set('d'); + term_vi_unset('y'); + term_select(); + term_operate(); + vselm = 0; + } else if (term_vi_flag('d')) { + accum++; + savex = cursorx+left; + savey = cursory+base; + cursorx = 0; left = 0; + term_ex_motion = term_vi_line; + term_operate(); + cursorx = savex-left; + cursory = savey-base; + } else { + term_vi_set('d'); + term_vi_unset('y'); + } + } else if (kc == '>' || kc == '<') { + if (vselm) { + term_vi_unset('<'); + term_vi_unset('>'); + term_vi_set(kc); + term_vi_unset('d'); + term_vi_unset('y'); + term_operate(); + vselm = 0; + } else if (term_vi_flag(kc)) { + accum++; + savex = cursorx+left; + savey = cursory+base; + cursorx = 0; left = 0; + term_ex_motion = term_vi_line; + term_operate(); + cursorx = savex-left; + cursory = savey-base; + term_vi_unset('<'); + term_vi_unset('>'); + } else { + term_vi_unset('<'); + term_vi_unset('>'); + term_vi_set(kc); + term_vi_unset('d'); + term_vi_unset('y'); + } + + } else if (kc == 'E') { + term_ex_motion = term_vi_ee; + term_operate(); + } else if (kc == 'e') { + term_ex_motion = term_vi_e; + term_operate(); + + } else if (kc == 'F' || kc == 'f') { + // does nothing here + } else if (kc == 'G') { + term_ex_motion = term_vi_eof; + term_operate(); + + } else if (kc == 'g' && lk == 'g') { + term_ex_motion = term_vi_top; + term_operate(); + + } else if (kc == 'H') { + term_ex_motion = term_vi_hh; + term_operate(); + + } else if (kc == 'h') { + term_ex_motion = term_vi_h; + term_operate(); + + } else if (kc == 'I') { + term_save_undo(); + if (vselm) { + term_vi_set('d'); + term_vi_unset('y'); + term_select(); + term_operate(); + vselm = 0; + } + term_setmode(1); + cursorx = 0; + left = 0; + } else if (kc == 'i') { + term_save_undo(); + if (vselm) { + term_vi_set('d'); + term_vi_unset('y'); + term_select(); + term_operate(); + vselm = 0; + } + term_setmode(1); + } else if (kc == 'J') { + term_save_undo(); + term_justify(); + } else if (kc == 'j') { + term_ex_motion = term_vi_j; + term_operate(); + } else if (kc == 'K') { + // err.. + } else if (kc == 'k') { + term_ex_motion = term_vi_k; + term_operate(); + } else if (kc == 'L') { + term_ex_motion = term_vi_ll; + term_operate(); + } else if (kc == 'l') { + term_ex_motion = term_vi_l; + term_operate(); + } else if (kc == 'M') { + term_ex_motion = term_vi_mm; + term_operate(); + } else if (kc == 'm') { + // set mark + } else if (kc == 'N') { + if (lastsearch == '') { + statustext = 'No previous regular expression'; + } else { + if (!accum) accum = 1; + var sl = lastsearch; + var cx = lastsearch.substr(0, 1); + cx = (cx == '?') ? '/' : '?'; + var i; + for (i = 0; i < accum; i++) { + term_command(cx+(sl.substr(1, sl.length-1))); + } + lastsearch = sl; + accum = 0; + } + } else if (kc == 'n') { + if (lastsearch == '') { + statustext = 'No previous regular expression'; + } else { + if (!accum) accum = 1; + var i; + for (i = 0; i < accum; i++) + term_command(lastsearch); + accum = 0; + } + } else if (kc == 'O') { + cursorx = 0; + left = 0; + term_insert(cursory+base); + term_setmode(1); + } else if (kc == 'o') { + cursory++; + cursorx = 0; + left = 0; + term_insert(cursory+base); + term_setmode(1); + term_save_undo(); + } else if (kc == 'P' || kc == 'p') { + term_save_undo(); + term_paste(kc == 'P' ? false : true); + accum = 0; + + } else if (kc == 'R') { + term_save_undo(); + if (vselm) { + term_vi_set('d'); + term_vi_unset('y'); + term_select(); + term_operate(); + vselm = 0; + } + term_setmode(2); + + } else if (kc == 'S') { + term_vi_set('d'); + term_vi_unset('c'); + savex = cursorx+left; + savey = cursory+base; + cursorx = 0; left = 0; + term_ex_motion = term_vi_line; + term_operate(); + cursorx = savex-left; + cursory = savey-base; + term_setmode(1); + + } else if (kc == 's') { + term_vi_set('d'); + term_vi_unset('c'); + savex = cursorx+left; + savey = cursory+base; + term_ex_motion = term_vi_l; + term_operate(); + cursorx = savex-left; + cursory = savey-base; + term_setmode(1); + + } else if (kc == 'T' || kc == 't') { + // nothing here + } else if (kc == 'V' || kc == 'v') { + if (vselm) { + vselm = 0; + vselx = undefined; + vsely = undefined; + } else { + vselm = (kc == 'v') ? 1 : 2; + vselx = cursorx+left; + vsely = cursory+base; + } + + } else if (kc == 'W') { + term_ex_motion = term_vi_ww; + term_operate(); + } else if (kc == '{') { + term_ex_motion = term_vi_ob; + term_operate(); + } else if (kc == '}') { + term_ex_motion = term_vi_cb; + term_operate(); + } else if (kc == 'w') { + term_ex_motion = term_vi_w; + term_operate(); + } else if (kc == 'x' || kc == 'X') { + if (term_vi_set('d')) { + term_vi_unset('d'); + } else { + term_vi_set('d'); + if (kc == 'x') { + term_ex_motion = term_vi_l; + } else { + term_ex_motion = term_vi_h; + } + term_operate(); + } + } else if (kc == 'y') { + if (vselm) { + term_vi_set('y'); + term_vi_unset('d'); + term_select(); + term_operate(); + vselm = 0; + } else if (term_vi_flag('y')) { + accum++; + savex = cursorx+left; + savey = cursory+base; + cursorx = 0; left = 0; + term_ex_motion = term_vi_line; + term_operate(); + cursorx = savex-left; + cursory = savey-base; + } else { + term_vi_set('y'); + term_vi_unset('d'); + } + } else if (kc == 'Z' && lk == 'Z') { + editor_disable(true); + accum = 0; + } else if (k == 27 || (k == 91 && ctrl)) { // escape or ^[ + vselm = 0; + statustext = ''; + mode = 0; + } else { + accum = 0; + } + term_scrollto(); + } else if ((k == 27 || (k == 91 && ctrl))) { // escape or ^[ + + vselm = 0; + statustext = ''; + if (command != '') { + cursorx = savex; + cursory = savey; + } else { + if (accum > 1) { + var i, j; + j = lastreg; + lastreg = '.'; + for (i = 1; i < accum; i++) { + term_paste(false); + } + lastreg = j; + accum = 0; + } + cursorx--; + term_scrollto(); + } + mode = emacsen ? 1 : 0; + command = ''; + commandleft = 0; + } else if (command != '') { + if (k == 10 || k == 13) { + // cr and execute command + cursorx = savex; + cursory = savey; + if (emacsen) { + mode = 1; + } else { + mode = 0; + } + var cy = command; + + command = ''; + commandleft = 0; + + term_command(cy); + var cx = command.substr(0,1); + if (cx == '/' || cx == '?') lastsearch = cy; + else oldcommand = cy; + // incase its needed + term_scrollto(); + } else if (k == 8) { + if (!synth) return; + command = command.substr(command, command.length-1); + cursorx--; + if (cursorx <= 0) { + var w = term_cols; + commandleft -= (w-16); + cursorx = w - 8; + if (commandleft < 0) { + cursorx = savex; + cursory = savey; + if (emacsen) { + mode = 1; + } else { + mode = 0; + } + } + } + } else { + command = command + kc; + cursorx++; + var w = term_cols; + if (cursorx >= (w-8)) { + cursorx = 8; + commandleft += w-16; + } + } + term_redraw(); + term_calcx(); + _update_backing(); + return; + } else { + var t = (file[cursory+base]); + var lx = (t.substr(0, cursorx+left)); + var ly = (t.substr(cursorx+left+(mode-1))); + var lz = (t.substr(cursorx+left)); + var g = (tags[cursory+base]); + var gx = (g.substr(0, cursorx+left)); + var gy = (g.substr(cursorx+left+(mode-1))); + var gz = (g.substr(cursorx+left)); + if (!lx) lx = ''; + if (k == 9) kc = ' '; + if (k == 10 || k == 13) { + // CR + if (!lz) lz = ''; + file[cursory+base] = lx; + tags[cursory+base] = gx; + cursory++; + cursorx=0; + left=0; + term_insert(cursory+base); + file[cursory+base] = lz; + tags[cursory+base] = gz; + lastinsert += "\n"; + } else if (k == 8) { + if (!synth) return; + // backspace + file[cursory+base] = lx.substr(0,lx.length-1)+lz; + tags[cursory+base] = gx.substr(0,lx.length-1)+gz; + lastinsert = lastinsert.substr(0,lastinsert.length-1); + cursorx--; + } else { + if (kc == '') return; + file[cursory+base] = lx+kc+ly; + tags[cursory+base] = gx+String.fromCharCode(tagstyle)+gy; + lastinsert += kc; + cursorx+=kc.length; + } + term_scrollto(); + } + + term_redraw(); + _update_backing(); + return; + } + + function _redraw_cursor() { + term_draw_cursor(true); + _update_backing(); + } + + function term_draw_cursor(tf) { + // maybe use vertical bar if mode==1 hrm? + if (!tf || !cursor._opaque) { + cursor._opaque = true; + cursor.style.color = palette[1]; + cursor.style.backgroundColor = palette[0]; + } else { + cursor._opaque = false; + cursor.style.color = palette[0]; + cursor.style.backgroundColor = palette[1]; + } + } + + function _redraw_term_force() { + while (term.firstChild) term.removeChild(term.firstChild); + } + function _redraw_term() { + + var h = term_rows; + var w = term_cols; + + var ka, kb; + var tospell = 0; + var osp = ''; + var y; + var tago = new Array(); + var isurl = new Array(); + var ca, cb; + var sa, sb; + var qp; + if (vselm) { + sa = vsely-base; + sb = cursory; + if (sa > sb) { + zx = sa; + sa = sb; + sb = zx; + } + ca = vselx-left; + cb = cursorx; + if (ca > cb) { + zx = ca; + ca = cb; + cb = zx; + } + } + for (y = 0; y < h; y++) { + ka = ''; kb = ''; + var x = file[y+base]; + var g; + + var zleft = 0; + var cx; + var j, vj; + if (y == (h-1)) { + if (cursory == y) { + zleft = commandleft; + x = command; + cx = 0; + statustext = ''; + } else if (emacsen) { + x = '[' + (mode == 1 ? 'Ins' : 'Ovr') + '] ' + statustext; + while (x.length < w) { + x += ' '; + } + cx = 4; + } else if (vselm == 1) { + cx = 1; x = '-- VISUAL --'; + statustext = ''; + } else if (vselm == 2) { + cx = 1; x = '-- VISUAL LINE --'; + statustext = ''; + } else if (mode == 0) { + x = statustext; + cx = 16; + } else if (mode == 1) { + cx = 1; x = '-- INSERT --'; + statustext = ''; + } else if (mode == 2) { + cx = 1; x = '-- REPLACE --'; + statustext = ''; + } + g = _mxs(x.length, cx); + } else if (x == undefined) { + x = '~'; + g = "\010"; + } else { + zleft = left; + g = tags[y+base]; + // do spellchecking + var p = 0; + vj = x.split(/[ ,;]+/); + for (j = 0; j < vj.length; j++) { + var vx = vj[j]; + var vm = _word(vx); + if (j != 0) p++; + + if (vx.match(/^(https?|ftp):\/\//)) { + g = g.substr(0, p) + + _mxo(g.substr(p, vx.length), ((1+tago.length)*256)) + + g.substr(p+vx.length); + isurl[tago.length] = true; + tago[tago.length] = vx; + + } else if (brokenwords[vm] && !safewords[vm]) { + g = g.substr(0, p) + + _mxo(g.substr(p, vx.length), ((1+tago.length)*256)) + + g.substr(p+vx.length); + isurl[tago.length] = false; + tago[tago.length] = vx; + } else { + if (vm.length > 3 && !_safe(vm) && !safewords[vm] && !spelling) { + tospell++; + osp += escape("c"+tospell) + "=" + + escape(vm) + "&"; + spellcheck[tospell] = vm; + } + } + p += vx.length; + } + } + if (x == undefined) { + x = ''; + g = ''; + } + + var zx; + vx = 0; + + // truncate as necessary + x = x.substr(zleft, x.length-zleft); + g = g.substr(zleft, g.length-zleft); + if (x.length >= w) { + x = x.substr(0, w); + g = g.substr(0, w); + } + + if (vselm) { + if (vselm == 1 && sa == sb && sa == y) { + // middle of line between ca->cb is selected + g = g.substr(0,ca) + _mxo(g.substr(ca,(cb-ca)+1), 4) + g.substr(cb,(g.length-cb)-1); + } else if ((sa < y && sb > y) || (vselm == 2 && (sa <= y && sb >= y))) { + // entire line selected + g = _mxo(g, 4); + } else if (sa < y && sb == y) { + // beginning of line selected (up to q) + // if (sb is cursory) then q = cursorx otherwise vselx + qp = (sb == cursory) ? cursorx : (vselx-left); + g = _mxo(g.substr(0, qp+1), 4) + g.substr(qp, (g.length-qp)-1); + } else if (sa == y && sb > y) { + // end of line selected (beginning at q) + // if (sa is cursory) then q = cursorx otherwise vselx + qp = (sa == cursory) ? cursorx : (vselx-left); + g = g.substr(0, qp) + _mxo(g.substr(qp, g.length-qp), 4); + } + } + + vj = 0; + g += "\377"; // terminate + x += " "; + x = x.replace(/ /g, "\240"); + + if (term.childNodes.length > y) { + zx = term.childNodes[y]; + if (zx._cachex == x && zx._cacheg == g) { + if (y == cursory) _calcy(zx, x, g); + continue; + } + + // as a last ditch effort- to accelerate deletions... + // and inserts... + if (term.childNodes.length > (y+1)) { + var zy = term.childNodes[y+1]; + if (zy._cachex == x && zy._cacheg == g) { + // okay then, so the NEXT line is the winner + // copy its nodes + term.removeChild(zy); + term.replaceChild(zy, zx); + if (zy.nextSibling) { + term.insertBefore(zx, zy.nextSibling); + } else { + term.appendChild(zx); + } + if (y == cursory) _calcy(zy, x, g); + continue; + } + } + + // update + while (zx.firstChild) zx.removeChild(zx.firstChild); + } else { + zx = document.createElement('PRE'); + zx.style.display='block'; + zx.style.fontFamily = 'monospace'; + zx.style.fontSize = '100%'; + _zmp(zx); + zx.style.marginBottom = '1px'; + } + + cx = 255; + var ax = zx; + + for (j = 0; j < x.length; j++) { + var gx = g.charCodeAt(j); + + if (gx != cx) { + if (j != vj && ax) { + var t = x.substr(vj, (j-vj)); + if (!t) t = ''; + ax.appendChild(document.createTextNode(t)); + if (zx != ax) zx.appendChild(ax); + vj = j; + } + + if (gx > 255) { + var wx = parseInt(gx / 256)-1; + + ax = document.createElement('A'); + if (isurl[wx]) { + ax.style.borderBottom = '1px double blue'; + ax.href = tago[wx]; + ax.target = '_new'; + ax.ondblclick = _openurl; + } else { + ax.style.borderBottom = '1px dashed red'; + ax.href = 'javascript:void(0)'; + ax.ondblclick = _suggest; + } + ax._term = _word(tago[wx]); + ax._len = tago[wx].length; + ax._row = y+base; + ax._col = j+zleft; + ax.onclick = _subclick; + } else { + ax = document.createElement('SPAN'); + } + if (gx == 255) gx = 0; + if (gx & 1) { + ax.style.fontWeight = 'bold'; + } else { + ax.style.fontWeight = 'normal'; + } + if (gx & 2) { + ax.style.textDecoration = 'underline'; + } else { + ax.style.textDecoration = 'none'; + } + if (gx & 4) { // reverse video + ax.style.color = palette[1]; + ax.style.backgroundColor = palette[0]; + } else { + ax.style.color = palette[0]; + ax.style.backgroundColor = palette[1]; + } + if (gx & 8) { // unselectable + ax.unselectable = true; + if (ax.setAttribute) ax.setAttribute('unselectable', 'on'); + ax.style.color = palette[0]; + ax.style.backgroundColor = palette[1]; + ax.style.userSelect = 'none'; + ax.style['-moz-user-select'] = 'none'; + } + if (gx & 16) { + ax.style.fontStyle = 'italic'; + } else { + ax.style.fontStyle = 'normal'; + } + cx = gx; + } + } + if (j != vj) { + var t = x.substr(vj, (j-vj)-1); + ax.appendChild(document.createTextNode(t)); + if (zx != ax) zx.appendChild(ax); + vj = j; + } + ax.appendChild(document.createTextNode("\240")); + zx._cachex = x; + zx._cacheg = g; + if (term.childNodes.length <= y) { + term.appendChild(zx); + } + + if (y == 1) { + var qx = zx; + var nh = 0; + while (qx && qx != document.body) { + nh += qx.offsetTop; + qx = qx.offsetParent; + } + if (nh != line_height) { + line_height = nh; + term_resize(); + } + } + + if (y == cursory) { + _calcy(zx, x, g); + } + + ax = undefined; // break; + } + while (term.childNodes.length > h) { + term.removeChild(term.lastChild); + } + zx = undefined; // break + + if (!spelling && tospell > 0 && spell_script) { + spelling = true; + var xh = _xhttp(); + osp=osp.substr(0,osp.length-1); + xh.open("GET", spell_script+"?"+osp, true); + xh.onreadystatechange = function() { + if (xh.readyState == 4) { + var j; + var a = xh.responseText.split("\n"); + for (j = 0; j < a.length; j++) { + var kp = a[j].split("=", 2); + var k, v; + if (kp.length == 2) { + k = kp[0]; + v = kp[1]; + } else if (kp.length == 1) { + k = kp[0]; + v = ''; + } else { + k = a[j]; + v = ''; + } + if (k.substr(0,1) != 'c') continue; + k = k.substr(1, k.length-1); + var term = spellcheck[k]; + if (v == undefined || v == '') { + brokenwords[term] = true; + suggestions[term] = new Array(); + } else if (v == term) { + safewords[term] = true; + } else { + safewords[v] = true; + if (!suggestions[term]) { + suggestions[term] = new Array(); + } + suggestions[term][ suggestions[term].length ] = v; + brokenwords[term] = true; + } + } + spelling=false; + window.setTimeout(term_redraw,10); + xh = undefined; // break (deferred) + } + }; + xh.send(undefined); + } + _update_backing(); + } + + function _redraw_term_back() { + drawiv = undefined; + _redraw_term(); + term_draw_cursor(); + } + function term_redraw() { + if (drawiv) window.clearTimeout(drawiv); + drawiv = window.setTimeout(_redraw_term_back, 10); + } + + function term_resize() { + var h = term.offsetHeight; + var r = line_height; + if (!line_height) r = cursor.offsetHeight-1; // 1 px overlap + var nh = (h/r); + term_rows = parseInt(nh); + term_win_height = h; + + h = term.offsetWidth; + r = (term_cur_width+1); // 1 px padding + nh = (h/r); + term_cols = parseInt(nh); + term_win_width = h; + + term_redraw(); + } + function editor_disable(sav) { + if (cursoriv) { + window.clearInterval(cursoriv); + cursoriv = undefined; + } + + _cbrestore(); + + if (term._formelement) { + if (sav) term._formelement.value = term_freeze(); + + // disconnect + term._formelement = undefined; + } + + if (backing.removeEventListener) { + backing.removeEventListener('DOMAttrModified',_backing_paste,false); + backing.removeEventListener('Input',_backing_paste,false); + backing.removeEventListener('input',_backing_paste,false); + } + backing.oninput = undefined; + backing.onInput = undefined; + + document.body.removeChild(suggest); + document.body.removeChild(term); + document.body.removeChild(cursor); + + var z; + for (z = document.body.firstChild; z; z = z.nextSibling) { + if (z.tagName && z._flipe) z.style.display = z._orige; + } + + // bug in firefox: can't remove this + //document.body.removeChild(backing); + if (backing.blur) backing.blur(); + backing.style.display = 'none'; + if (document.body.focus) document.body.focus(); + if (document.focus) document.focus(); + + document.body.style.overflow = ''; + } + function _cursor_fix() { + term_cur_width = cursor.getBoundingClientRect().width; + } + function _zmp(o) { + o.style.marginTop='0px'; + o.style.marginLeft='0px'; + o.style.marginRight='0px'; + o.style.marginBottom='0px'; + o.style.paddingTop='0px'; + o.style.paddingLeft='0px'; + o.style.paddingRight='0px'; + o.style.paddingBottom='0px'; + } + return function(textarea, options) { + if (term && term._formelement && term._formelement != textarea) { + editor_disable(false); + } + if (options) { + if (typeof options.onSave == 'function') { + onSave = options.onSave; + } + if (typeof options.onExit == 'function') { + onExit = options.onExit; + } + } + + if (options && options.spell_script) { + spell_script = options.spell_script; + } + + // okay, find EVERYTHING inside body and display none it + /* + var z; + for (z = document.body.firstChild; z; z = z.nextSibling) { + if (z.tagName) { + z._orige = z.style.display; + z._flipe = true; + z.style.display = 'none'; + } + } + */ + + if (!term) { + term = document.createElement('DIV'); + suggest = document.createElement('DIV'); + backing = document.createElement('TEXTAREA'); + cursor = document.createElement('DIV'); + wrapper = document.createElement('DIV'); + } + + // if (document.documentElement) { + // _zmp(document.documentElement); + // document.documentElement.height = '100%'; + // _zmp(document.body); + // document.body.height = '100%'; + // } + + cursor.className = 'editorcursor'; + term.className = 'editor'; + + suggest.style.position = 'absolute'; + suggest.style.display = 'none'; + + backing.tabIndex = -1; + backing.style.position = 'absolute'; + backing.style.bottom = '0px'; + backing.style.right = '0px'; + backing.style.width = '1px'; + backing.style.height = '1px'; + backing.style.visibility = 'hidden'; + backing.oninput = _backing_paste; + backing.onInput = _backing_paste; + if (backing.addEventListener) { + backing.addEventListener('DOMAttrModified',_backing_paste,false); + backing.addEventListener('Input',_backing_paste,false); + backing.addEventListener('input',_backing_paste,false); + } + if (window.addEventListener) { + window.addEventListener('DOMMouseScroll',_mousescroll,false); + } + /* + tools.className = 'editortools'; + tools.style.position = 'absolute'; + tools.style.right = '0px'; + tools.style.bottom = '0px'; + tools.innerHTML = '' + + '' + + '' + + '' + + ' ' + + '' + + ' ' + + '' + + '' + //*/ + cursor.onclick = _pass_click; + cursor.ondblclick = _pass_dblclick; + + document.body.appendChild(suggest); + document.body.appendChild(term); + // firefox bug + if (once) document.body.appendChild(backing); + document.body.appendChild(cursor); + + cursoriv = window.setInterval(_redraw_cursor, 300); + + term.style.position = 'absolute'; + term.style.top = '0px'; + term.style.left = '0px'; + term.style.display = 'block'; + term.style.overflow = 'hidden'; + term.style.width = '100%'; + term.style.height = '100%'; + term.style.cursor = 'default'; + term.style.fontFamily = 'monospace'; + term.style.fontSize = '100%'; + _zmp(term); + term._formelement = textarea; + document.body.style.overflow = 'hidden'; + + _cbd('select', _cancel_ev); + _cbd('selectstart', _cancel_ev); + _cbd('keydown', term_keyfix); + _cbd('keypress', term_keypress); + if (browser.webkit) { + _cbd('keyup', term_keyup); + } + _cbd('paste', _msie_paste); + _cbd('click', _mouseclick); + _cbd('mousedown', _mousedown); + _cbd('mousemove', _mousemove); + _cbd('mouseup', _mouseup); + _cbd('mousewheel', _mousescroll); + + vselm = 0; + vseld = false; + vselx = undefined; + vsely = undefined; + + textarea.blur(); + cursorx = 0; + cursory = 0; + cursor.style.position = 'absolute'; + cursor.style.top = 0; + cursor.style.left = 0; + cursor.style.fontFamily = 'monospace'; + cursor.style.fontSize = '100%'; + cursor.style.width = 'auto'; + cursor.style.cursor = 'default'; + _zmp(cursor); + cursor.style.overflow = 'hidden'; + cursor.innerHTML = 'X'; + cursor._opaque = false; + cursor._lasty = -1; + cursor._lastx = -1; + cursor._lastch = '-xyz-'; + cursor._lastgh = 0; + palette = undefined; + if (options && options.color && options.backgroundColor) { + palette = new Array(); + palette[0] = options.color; + palette[1] = options.backgroundColor; + } else if (document.defaultView && document.defaultView.getComputedStyle) { + palette = new Array(); + var cs = document.defaultView.getComputedStyle(term, null); + palette[0] = cs.color; + palette[1] = cs.backgroundColor; + } else if (window.getComputedStyle) { + palette = new Array(); + var cs = window.getComputedStyle(term, null); + palette[0] = cs.color; + palette[1] = cs.backgroundColor; + } else if (term.currentStyle) { + palette = new Array(); + palette[0] = term.currentStyle.color; + palette[1] = term.currentStyle.backgroundColor; + } + + if (emacsen) { + mode = 1; + } else { + mode = 0; + } + + once = false; + + backing._lastvalue = ''; + backing.value = ''; + backing.defaultValue = ''; + suggest._visible = false; + suggest.display = 'none'; + + // degrading... + file = new Array(); + tags = new Array(); + var lines = textarea.value.split("\n"); + var j; + for (j = 0; j < lines.length; j++) { + if (lines[j].substr(lines[j].length-1, 1) == "\r") { + lines[j] = lines[j].substr(0, lines[j].length-1); + } + var aa = _hra(lines[j]); + file[j] = aa[0]; + tags[j] = aa[1]; + } + // fix + textarea.value = term_freeze(); + + cursor.style.display = 'inline'; + _cursor_fix(); + window.setTimeout(term_redraw,1); + term_resize(); + + _cbw('resize', term_resize); + _update_backing(); + + // public API + return { + freeze: term_freeze, + thaw: term_thaw, + setmode: term_setmode, + roll_yank: term_roll_yank, + justify: term_justify, + vi_bb: term_vi_bb, + vi_b: term_vi_b, + vi_tt: term_vi_tt, + vi_t: term_vi_t, + vi_ff: term_vi_ff, + vi_f: term_vi_f, + vi_eof: term_vi_eof, + vi_top: term_vi_top, + vi_h: term_vi_h, + vi_j: term_vi_j, + vi_k: term_vi_k, + vi_l: term_vi_l, + vi_ll: term_vi_ll, + vi_mm: term_vi_mm, + vi_hh: term_vi_hh, + vi_ob: term_vi_ob, + vi_cb: term_vi_cb, + vi_ww: term_vi_ww, + vi_w: term_vi_w, + vi_flag: term_vi_flag, + vi_unset: term_vi_unset, + vi_set: term_vi_set, + vi_bounce: term_vi_bounce, + vi_eol: term_vi_eol, + vi_line: term_vi_line, + vi_ee: term_vi_ee, + vi_e: term_vi_e, + vi_v: term_vi_v, + vi_vv: term_vi_vv, + select: term_select, + indent: term_indent, + unindent: term_unindent, + operate: term_operate, + save_undo_line: term_save_undo_line, + save_undo: term_save_undo, + 'delete': term_delete, + skipreverse2: term_skipreverse2, + skipforward: term_skipforward, + skipbackward: term_skipbackward, + search: term_search, + rsearch: term_rsearch, + command: term_command, + calcy: term_calcy, + calcx: term_calcx, + scrollto: term_scrollto, + insert: term_insert, + paste: term_paste, + keyfix: term_keyfix, + keypress: term_keypress, + keypress_inner: term_keypress_inner, + draw_cursor: term_draw_cursor, + redraw: term_redraw, + resize: term_resize, + disable: editor_disable + }; + }; + +})();