(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0; i--) { this.unbindTextarea(this.textfields[i].editor) } for (let i = this.aceInstances.length - 1; i >= 0; i--) { this.unbindAce(this.aceInstances[i].editor) } } unbindAce (ace) { var i = this.aceInstances.findIndex(function (binding) { return binding.editor === ace }) if (i >= 0) { var binding = this.aceInstances[i] this.unobserve(binding.yCallback) binding.editor.off('change', binding.aceCallback) this.aceInstances.splice(i, 1) } } bindAce (ace, options) { var self = this // this function makes sure that either the // ace event is executed, or the yjs observer is executed var token = true function mutualExcluse (f) { if (token) { token = false try { f() } catch (e) { token = true throw new Error(e) } token = true } } ace.markers = [] var disableMarkers = false if (typeof options === 'object') { if (typeof options.disableMarkers !== 'undefined') { disableMarkers = options.disableMarkers } } ace.setValue(this.toString()) function aceCallback (delta) { mutualExcluse(function () { var start = 0 var length = 0 var aceDocument = ace.getSession().getDocument() if (delta.action === 'insert') { start = aceDocument.positionToIndex(delta.start, 0) self.insert(start, delta.lines.join('\n')) } else if (delta.action === 'remove') { start = aceDocument.positionToIndex(delta.start, 0) length = delta.lines.join('\n').length self.delete(start, length) } }) } ace.on('change', aceCallback) if (!disableMarkers) { if (this.inteval) { clearInterval(this.inteval) } this.inteval = setInterval(function () { var i = 0 var now = Date.now() var timeVisible = 800 while (i < ace.markers.length) { var marker = ace.markers[i] if (marker.timestamp + timeVisible < now) { ace.getSession().removeMarker(marker.id) ace.markers.splice(i, 1) i-- } i++ } }, 1000) } var Range = window.ace.require('ace/range').Range function setMarker (start, end, klazz) { if (disableMarkers) { return } var offset = 0 if (start.row === end.row && start.column === end.column) { offset = 1 } var range = new Range(start.row, start.column, end.row, end.column + offset) var marker = ace.session.addMarker(range, klazz, 'text') ace.markers.push({id: marker, timestamp: Date.now()}) } function yCallback (events) { var aceDocument = ace.getSession().getDocument() mutualExcluse(function () { for (var i = 0; i < events.length; i++) { var event = events[i] if (event.type === 'insert') { let start = aceDocument.indexToPosition(event.index, 0) let end = aceDocument.indexToPosition(event.index + event.value.length, 0) aceDocument.insert(start, event.value) setMarker(start, end, 'inserted') } else if (event.type === 'delete') { let start = aceDocument.indexToPosition(event.index, 0) let end = aceDocument.indexToPosition(event.index + event.length, 0) var range = new Range(start.row, start.column, end.row, end.column) aceDocument.remove(range) setMarker(start, end, 'deleted') } } }) } this.observe(yCallback) this.aceInstances.push({ editor: ace, yCallback: yCallback, aceCallback: aceCallback }) } bind () { var e = arguments[0] if (e instanceof Element) { this.bindTextarea.apply(this, arguments) } else if (e != null && e.session != null && e.getSession != null && e.setValue != null) { this.bindAce.apply(this, arguments) } else { console.error('Cannot bind, unsupported editor!') } } unbindTextarea (textarea) { var i = this.textfields.findIndex(function (binding) { return binding.editor === textarea }) if (i >= 0) { var binding = this.textfields[i] this.unobserve(binding.yCallback) var e = binding.editor e.onkeydown = null e.onkeyup = null e.onkeypress = null e.onpaste = null e.oncut = null this.textfields.splice(i, 1) } } bindTextarea (textfield, domRoot) { domRoot = domRoot || window; // eslint-disable-line if (domRoot.getSelection == null) { domRoot = window; // eslint-disable-line } // don't duplicate! for (var t = 0; t < this.textfields.length; t++) { if (this.textfields[t].editor === textfield) { return } } var creatorToken = false var word = this textfield.value = this.toString() var createRange, writeRange, writeContent if (textfield.selectionStart != null && textfield.setSelectionRange != null) { createRange = function (fix) { var left = textfield.selectionStart var right = textfield.selectionEnd if (fix != null) { left = fix(left) right = fix(right) } return { left: left, right: right } } writeRange = function (range) { writeContent(word.toString()) textfield.setSelectionRange(range.left, range.right) } writeContent = function (content) { textfield.value = content } } else { createRange = function (fix) { var range = {} var s = domRoot.getSelection() var clength = textfield.textContent.length range.left = Math.min(s.anchorOffset, clength) range.right = Math.min(s.focusOffset, clength) if (fix != null) { range.left = fix(range.left) range.right = fix(range.right) } var editedElement = s.focusNode if (editedElement === textfield || editedElement === textfield.childNodes[0]) { range.isReal = true } else { range.isReal = false } return range } writeRange = function (range) { writeContent(word.toString()) var textnode = textfield.childNodes[0] if (range.isReal && textnode != null) { if (range.left < 0) { range.left = 0 } range.right = Math.max(range.left, range.right) if (range.right > textnode.length) { range.right = textnode.length } range.left = Math.min(range.left, range.right) var r = document.createRange(); // eslint-disable-line r.setStart(textnode, range.left) r.setEnd(textnode, range.right) var s = window.getSelection(); // eslint-disable-line s.removeAllRanges() s.addRange(r) } } writeContent = function (content) { var contentArray = content.replace(new RegExp('\n', 'g'), ' ').split(' '); // eslint-disable-line textfield.innerText = '' for (var i = 0; i < contentArray.length; i++) { var c = contentArray[i] textfield.innerText += c if (i !== contentArray.length - 1) { textfield.innerHTML += ' ' } } } } writeContent(this.toString()) function yCallback (events) { for (var e = 0; e < events.length; e++) { var event = events[e] if (!creatorToken) { var oPos, fix if (event.type === 'insert') { oPos = event.index fix = function (cursor) { // eslint-disable-line if (cursor <= oPos) { return cursor } else { cursor += 1 return cursor } } var r = createRange(fix) writeRange(r) } else if (event.type === 'delete') { oPos = event.index fix = function (cursor) { // eslint-disable-line if (cursor < oPos) { return cursor } else { cursor -= 1 return cursor } } r = createRange(fix) writeRange(r) } } } } this.observe(yCallback) // consume all text-insert changes. textfield.onkeypress = function (event) { if (word.is_deleted) { // if word is deleted, do not do anything ever again textfield.onkeypress = null return true } creatorToken = true var char if (event.keyCode === 13) { char = '\n' } else if (event.key != null) { if (event.charCode === 32) { char = ' ' } else { char = event.key } } else { char = window.String.fromCharCode(event.keyCode); // eslint-disable-line } if (char.length > 1) { return true } else if (char.length > 0) { var r = createRange() var pos = Math.min(r.left, r.right, word.length) var diff = Math.abs(r.right - r.left) word.delete(pos, diff) word.insert(pos, char) r.left = pos + char.length r.right = r.left writeRange(r) } event.preventDefault() creatorToken = false return false } textfield.onpaste = function (event) { if (word.is_deleted) { // if word is deleted, do not do anything ever again textfield.onpaste = null return true } event.preventDefault() } textfield.oncut = function (event) { if (word.is_deleted) { // if word is deleted, do not do anything ever again textfield.oncut = null return true } event.preventDefault() } // // consume deletes. Note that // chrome: won't consume deletions on keypress event. // keyCode is deprecated. BUT: I don't see another way. // since event.key is not implemented in the current version of chrome. // Every browser supports keyCode. Let's stick with it for now.. // textfield.onkeydown = function (event) { creatorToken = true if (word.is_deleted) { // if word is deleted, do not do anything ever again textfield.onkeydown = null return true } var r = createRange() var pos = Math.min(r.left, r.right, word.toString().length) var diff = Math.abs(r.left - r.right) if (event.keyCode != null && event.keyCode === 8) { // Backspace if (diff > 0) { word.delete(pos, diff) r.left = pos r.right = pos writeRange(r) } else { if (event.ctrlKey != null && event.ctrlKey) { var val = word.toString() var newPos = pos var delLength = 0 if (pos > 0) { newPos-- delLength++ } while (newPos > 0 && val[newPos] !== ' ' && val[newPos] !== '\n') { newPos-- delLength++ } word.delete(newPos, pos - newPos) r.left = newPos r.right = newPos writeRange(r) } else { if (pos > 0) { word.delete(pos - 1, 1) r.left = pos - 1 r.right = pos - 1 writeRange(r) } } } event.preventDefault() creatorToken = false return false } else if (event.keyCode != null && event.keyCode === 46) { // Delete if (diff > 0) { word.delete(pos, diff) r.left = pos r.right = pos writeRange(r) } else { word.delete(pos, 1) r.left = pos r.right = pos writeRange(r) } event.preventDefault() creatorToken = false return false } else { creatorToken = false return true } } this.textfields.push({ editor: textfield, yCallback: yCallback }) } } Y.extend('Text', new Y.utils.CustomType({ name: 'Text', class: YText, struct: 'List', initType: function * YTextInitializer (os, model) { var _content = yield* Y.Struct.List.map.call(this, model, function (c) { return { id: JSON.stringify(c.id), val: c.content } }) return new YText(os, model.id, _content) } })) }) } module.exports = extend if (typeof Y !== 'undefined') { extend(Y) } },{}]},{},[1])