')
+ .css({
+ padding: '10% 20% 10% 10%',
+ fontFamily: 'Georgia, Palatino, DejaVu Serif, Book Antiqua, Palatino Linotype, Times New Roman, serif',
+ fontSize: '20px',
+ lineHeight: '30px'
+ })
+ .appendTo($body),
+ $scroll = Ox.$('
')
+ .css({
+ position: 'fixed',
+ right: '24px',
+ top: '16px',
+ width: '7%',
+ bottom: '16px',
+ overflow: 'hidden'
+ })
+ .appendTo($body),
+ $scrollText = Ox.$('
')
+ .css({
+ fontSize: '2px',
+ lineHeight: '3px',
+ cursor: 'pointer',
+ WebkitUserSelect: 'none'
+ })
+ .on({
+ mousedown: function(e) {
+ var offset = 'offsetY' in e ? e.offsetY : e.layerY;
+ document.documentElement.scrollTop = offset / factor + margin - 16;
+ }
+ })
+ .appendTo($scroll),
+ factor, margin;
+ text = Ox.encodeHTMLEntities(text)
+ .replace(/\r\n/g, '\n')
+ .replace(/[\r\n]/g, '
');
+ $text.html(text);
+ $scrollText.html(text);
+ window.onresize = function() {
+ margin = $text.width() * 0.1;
+ factor = $scrollText[0].clientHeight / $text[0].clientHeight;
+ };
+ window.onscroll = function() {
+ $scroll[0].scrollTop = (window.pageYOffset - margin + 16) * factor;
+ };
+ window.onresize();
+ });
+ });
+};
-txtjs.mark = function(notes) {
- notes.forEach(function(note) {
- if (!txtjs.notes.includes(note)) {
- txtjs.notes.push(note)
- }
- txtjs.renderNote(note)
- })
-}
-
-txtjs.notes = []
-
-;(function() {
- let listener = addEventListener || attachEvent
- let event = addEventListener ? 'message' : 'onmessage'
- listener(event, function(e) {
- let message = JSON.parse(e.message || e.data)
- txtjs.onMessage(message.action, message.data)
- })
-}())
-
-txtjs.addNoteFromSelection = function() {
- let note = txtjs.getNoteFromSelection()
- if (!note || txtjs.noteExists(note)) {
- return
- }
- txtjs.renderNote(note)
- txtjs.notes.push(note)
- txtjs.selectNote(note.id)
- getSelection().removeAllRanges()
- txtjs.postMessage('addNote', note)
-}
-
-txtjs.cancelEdit = function() {
- let editing = document.querySelector('g.editing')
- editing && editing.classList.remove('editing')
-}
-
-txtjs.createSVGElement = function(name) {
- return document.createElementNS('http://www.w3.org/2000/svg', name)
-}
-
-txtjs.editNote = function() {
- let editing = document.querySelector('g.editing')
- let note = txtjs.getNoteFromSelection()
- if (!editing || !note) {
- return
- }
- let id = txtjs.getNoteId(editing)
- note = Object.assign(Ox.getObjectById(txtjs.notes, id), {
- position: note.position,
- text: note.text
- })
- document.querySelector('svg').removeChild(editing)
- txtjs.renderNote(note)
- txtjs.selectNote(note.id)
- txtjs.postMessage('editNote', {
- id: id,
- position: note.position,
- text: note.text
- })
-}
-
-txtjs.getNewId = function() {
- let ids = txtjs.notes.map(function(note) {
- return note.id
- })
- let i = 1
- while (ids.includes(Ox.encodeBase26(i))) {
- i++
- }
- return Ox.encodeBase26(i)
-}
-
-txtjs.getNoteId = function(element) {
- let classNames = Array.from(element.classList).filter(function(className) {
- return className.startsWith('note-')
- })
- if (classNames.length == 0) {
- return
- }
- return classNames[0].substr(5)
-}
-
-txtjs.getNoteFromSelection = function() {
- let selection = getSelection()
- try {
- var range = selection.getRangeAt(0)
- } catch(e) {
- return
- }
- if (range.collapsed) {
- return
- }
- let container = range.commonAncestorContainer
- if (container.id != 'txt') {
- while (container != document.body) {
- container = container.parentElement
- if (container.id == 'txt') {
- break
- }
- }
- }
- if (container.id != 'txt') {
- return
- }
- let position = txtjs.getPosition(range)
- let pos = position.split(':').map(function(v) {
- return parseInt(v)
- })
- let note = {
- id: txtjs.getNewId(),
- position: position,
- text: txtjs.text.substr(pos[0], pos[1] - pos[0]),
- editable: true
- }
- if (txtjs.noteExists(note)) {
- return
- }
- return note
-}
-
-txtjs.getPosition = function(range) {
- let container = document.querySelector('#txt')
- let nodes = Array.from(container.childNodes)
- let startNodeIndex = nodes.indexOf(range.startContainer)
- let endNodeIndex = nodes.indexOf(range.endContainer)
- let index = 0
- let start = 0
- let end = 0
- for (let i = 0; i <= endNodeIndex; i++) {
- if (i == startNodeIndex) {
- start = index + range.startOffset
- }
- if (i == endNodeIndex) {
- end = index + range.endOffset
- }
- if (nodes[i].nodeType == 1) { //
- index++
- } else {
- index += nodes[i].textContent.length
- }
- }
- return start + ':' + end
-}
-
-txtjs.getRange = function(id, start, end) {
- let startContainer, startOffset, endContainer, endOffset
- let container = document.querySelector('#' + id)
- let nodes = Array.from(container.childNodes)
- let index = 0
- for (let i = 0; i < nodes.length; i++) {
- if (start < index + nodes[i].textContent.length && startOffset === void 0) {
- startContainer = nodes[i]
- startOffset = start - index
- }
- if (end <= index + nodes[i].textContent.length) {
- endContainer = nodes[i]
- endOffset = end - index
- break
- }
- if (nodes[i].nodeType == 1) { //
- index++
- } else {
- index += nodes[i].textContent.length
- }
- }
- let range = document.createRange()
- range.setStart(startContainer, startOffset)
- range.setEnd(endContainer, endOffset)
- return range
-}
-
-txtjs.getSelectedNote = function() {
- let elements = Array.from(document.querySelectorAll('g.selected'))
- if (elements.length == 0) {
- return
- }
- let id = txtjs.getNoteId(elements[0])
- return Object.assign(Ox.getObjectById(txtjs.notes, id), {
- elements: elements
- })
-}
-
-txtjs.moveNote = function() {
- let selected = txtjs.getSelectedNote()
- if (!selected || !selected.elements[0].classList.contains('editable')) {
- return
- }
- selected.elements.forEach(function(element) {
- element.classList.add('editing')
- })
-}
-
-txtjs.noteExists = function(note) {
- return txtjs.notes.some(function(note_) {
- return note_.position == note.position
- })
-}
-
-txtjs.onMessage = function(action, data) {
-
-}
-
-txtjs.postMessage = function(action, data) {
- console.log('postMessage', action, data)
- parent.postMessage(JSON.stringify({action: action, data: data}), '*')
-}
-
-txtjs.removeNote = function() {
- let selected = txtjs.getSelectedNote()
- if (!selected) {
- return
- }
- let id = txtjs.getNoteId(selected.elements[0])
- selected.elements.forEach(function(element) {
- element.parentElement.removeChild(element)
- })
- let index = txtjs.notes.map(function(note) {
- return note.id
- }).indexOf(id)
- txtjs.notes.splice(index, 1)
- txtjs.postMessage('removeNote', {
- id: id
- })
-}
-
-txtjs.renderNote = function(note) {
- let pos = note.position.split(':').map(function(v) {
- return parseInt(v)
- })
- let ids = ['txt', 'txt-scroll']
- ids.forEach(function(id) {
- let range = txtjs.getRange(id, pos[0], pos[1])
- let rects = Array.from(range.getClientRects())
- let size = rects.reduce(function(width, rect) {
- return width + rect.width
- }, 0)
- let maxHeight = 8192
- let firstIndex = Math.floor((rects[0].top + window.pageYOffset) / maxHeight)
- let lastIndex = Math.floor((rects[rects.length - 1].top + window.pageYOffset + rects[rects.length - 1].height) / maxHeight)
- for (let index = firstIndex; index <= lastIndex; index++) {
- let g = txtjs.createSVGElement('g')
- g.classList.add('note-' + note.id)
- g.classList.add('selectable')
- if (note.editable) {
- g.classList.add('editable')
- }
- g.setAttribute('data-size', size)
- g.setAttribute('pointer-events', id == 'txt' ? 'all' : 'none')
- rects.forEach(function(rect) {
- let element = txtjs.createSVGElement('rect')
- let x = id == 'txt' ? rect.left
- : rect.left - document.querySelector('#scroll').getBoundingClientRect().left + 8
- let y = id == 'txt' ? rect.top + window.pageYOffset - index * maxHeight
- : rect.top + document.querySelector('#scroll').scrollTop - 16 - index * maxHeight
- element.setAttribute('x', x)
- element.setAttribute('y', y)
- element.setAttribute('width', rect.width)
- element.setAttribute('height', rect.height)
- g.appendChild(element)
- })
- let svg = document.querySelector('svg#svg-' + id + '-' + index)
- for (let i = 0; i < svg.children.length; i++) {
- let childSize = parseInt(svg.children[i].getAttribute('data-size'))
- if (size > childSize) {
- svg.insertBefore(g, svg.children[i])
- break
- }
- }
- if (!g.parentElement) {
- svg.appendChild(g)
- }
- }
- })
-}
-
-txtjs.renderSVG = function(id, index, width, height) {
- function mousedown(e) {
- svg.addEventListener('mouseup', mouseup)
- timeout = setTimeout(function() {
- svg.removeEventListener('mouseup', mouseup)
- e.target.classList.remove('selectable')
- e.target.setAttribute('pointer-events', 'none')
- document.addEventListener('mouseup', function() {
- e.target.classList.add('selectable')
- e.target.setAttribute('pointer-events', 'all')
- })
- }, 250)
- }
- function mouseup(e) {
- clearTimeout(timeout)
- txtjs.selectNote(txtjs.getNoteId(e.target.parentElement))
- }
- let timeout
- let svg = txtjs.createSVGElement('svg')
- svg.setAttribute('id', 'svg-' + id + '-' + index)
- svg.setAttribute('pointer-events', 'none')
- if (id == 'txt') {
- svg.style.left = 0
- } else {
- svg.style.right = '8px'
- }
- svg.style.top = index * 8192 + 'px'
- svg.style.width = width + 'px'
- svg.style.height = height + 'px'
- if (id == 'txt') {
- svg.addEventListener('mousedown', mousedown)
- }
- let parentElement = id == 'txt' ? document.body : document.querySelector('#scroll')
- parentElement.appendChild(svg)
-}
-
-txtjs.renderSVGs = function() {
- let maxHeight = 8192
- let ids = ['txt', 'txt-scroll']
- ids.forEach(function(id) {
- let rect = document.querySelector('#' + id).getBoundingClientRect()
- let lastHeight = rect.height % maxHeight
- let n = Math.ceil(rect.height / maxHeight)
- for (let i = 0; i < n; i++) {
- txtjs.renderSVG(id, i, rect.width, i < n - 1 ? maxHeight : lastHeight)
- }
- })
-}
-
-txtjs.renderText = function(text) {
- txtjs.text = text
- html = text.replace(//g, '>')
- html = html.replace().replace(/\r\n/g, '\n').replace(/[\r\n]/g, '
')
- txtjs.html = Ox.encodeHTMLEntities(text).replace(/\r\n/g, '\n').replace(/[\r\n]/g, '
')
- window.addEventListener('resize', onResize)
- window.addEventListener('resizeend', onResizeend)
- window.addEventListener('scroll', onScroll)
- document.addEventListener('keydown', function(e) {
- console.log(e.keyCode)
- if (e.keyCode == 13) { // ENTER
- txtjs.editNote()
- } else if (e.keyCode == 27) { // ESCAPE
- txtjs.selectNote(null)
- txtjs.cancelEdit()
- } else if (e.keyCode == 37) { // LEFT
- txtjs.selectNextNote(-1)
- } else if (e.keyCode == 39) { // RIGHT
- txtjs.selectNextNote(1)
- } else if (e.keyCode == 46) { // DELETE
- txtjs.removeNote()
- } else if (e.keyCode == 77) { // M
- txtjs.moveNote()
- } else if (e.keyCode == 78) { // N
- txtjs.addNoteFromSelection()
- }
- })
- let style = document.createElement('style')
- style.innerText = [
- 'svg { mix-blend-mode: multiply; position: absolute }',
- 'g { fill: rgb(255, 255, 192); fill-opacity: 0.5 }',
- 'g.selectable { cursor: pointer }',
- 'g.editable { fill: rgb(255, 255, 0) }',
- 'g.selected { fill: rgb(224, 240, 255) }',
- 'g.editable.selected { fill: rgb(128, 192, 255) }',
- 'g.editable.editing { fill: rgb(128, 255, 128) }',
- '::selection { background: rgb(192, 192, 192) }'
- ].join('\n')
- document.head.appendChild(style)
- document.body.style.backgroundColor = 'rgb(255, 255, 255)'
- document.body.style.margin = 0
- document.body.style.overflowX = 'hidden'
- let textElement = document.createElement('div')
- textElement.id = 'txt'
- textElement.style.fontFamily = 'Georgia, Palatino, DejaVu Serif, Book Antiqua, Palatino Linotype, Times New Roman, serif',
- textElement.style.fontSize = '20px'
- textElement.style.lineHeight = '30px'
- textElement.style.padding = '10% 20% 10% 10%'
- textElement.innerHTML = txtjs.html
- textElement.addEventListener('mousedown', function() {
- txtjs.selectNote(null)
- })
- document.body.appendChild(textElement)
- let scrollElement = document.createElement('div')
- scrollElement.id = 'scroll'
- scrollElement.style.bottom = '16px'
- scrollElement.style.overflow = 'hidden'
- scrollElement.style.position = 'fixed'
- scrollElement.style.right = '24px'
- scrollElement.style.width = '7%'
- scrollElement.style.top = '16px'
- document.body.appendChild(scrollElement)
- let scrollTextElement = document.createElement('div')
- scrollTextElement.id = 'txt-scroll'
- scrollTextElement.style.cursor = 'pointer'
- scrollTextElement.style.fontFamily = 'Georgia, Palatino, DejaVu Serif, Book Antiqua, Palatino Linotype, Times New Roman, serif',
- scrollTextElement.style.fontSize = '2px'
- scrollTextElement.style.lineHeight = '3px'
- scrollTextElement.style.MozUserSelect = 'none'
- scrollTextElement.style.WebkitUserSelect = 'none'
- scrollTextElement.innerHTML = txtjs.html
- scrollTextElement.addEventListener('mousedown', function(e) {
- let offset = 'offsetY' in e ? e.offsetY : e.layerY
- document.documentElement.scrollTop = offset / factor + margin - 16
- })
- scrollElement.appendChild(scrollTextElement)
- txtjs.renderSVGs()
- let factor, margin
- onResize()
- function onResize() {
- factor = scrollTextElement.clientHeight / textElement.clientHeight
- margin = textElement.offsetWidth * 0.1
- setTimeout(function() {
- Array.from(document.querySelectorAll('svg')).forEach(function(svg) {
- svg.parentElement.removeChild(svg)
- })
- txtjs.renderSVGs()
- txtjs.mark(txtjs.notes)
- })
- }
- function onResizeend() {
-
- }
- function onScroll() {
- scrollElement.scrollTop = (window.pageYOffset - margin + 16) * factor
- }
- txtjs.mark([{id: 'A', position: '417:468', editable: false}])
-}
-
-txtjs.selectNextNote = function(direction) {
- let selected = txtjs.getSelectedNote()
- if (!selected) {
- return
- }
- let id = txtjs.getNoteId(selected.elements[0])
- let ids = txtjs.notes.sort(function(a, b) {
- return parseInt(a.position.split()[0]) - parseInt(b.position.split()[0])
- }).map(function(note) {
- return note.id
- })
- txtjs.selectNote(ids[Ox.mod(ids.indexOf(id) + direction, ids.length)])
-}
-
-txtjs.selectNote = function(id) {
- let selected = txtjs.getSelectedNote()
- if (selected) {
- selected.elements.forEach(function(element) {
- element.classList.remove('selected')
- })
- }
- if (id) {
- let editing = Array.from(document.querySelectorAll('g.editing'))
- if (editing.length && txtjs.getNoteId(editing) != id) {
- editing.forEach(function(element) {
- element.classList.remove('editing')
- })
- }
- let elements = Array.from(document.querySelectorAll('g.note-' + id))
- elements.forEach(function(element) {
- element.classList.add('selected')
- })
- // FIXME: SCROLL
- // elements[0].scrollIntoView()
- // window.scrollTo(0, elements[0].offsetTop)
- }
- txtjs.postMessage('selectNote', {id: id})
-}