Skip to content

Instantly share code, notes, and snippets.

@NetOpWibby
Created December 23, 2017 05:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NetOpWibby/aa9ec6a89981474e3503a96286d7ab7c to your computer and use it in GitHub Desktop.
Save NetOpWibby/aa9ec6a89981474e3503a96286d7ab7c to your computer and use it in GitHub Desktop.
I struggled with webpack to make this work. UGH.
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else {
var a = factory();
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 5);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return clean; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_priority__ = __webpack_require__(2);
function clean(updatedBlock) {
for (var key in updatedBlock.ranges) {
updatedBlock.ranges[key] = cleanRanges(updatedBlock.ranges[key]);
}
return updatedBlock;
};
function cleanRanges(ranges) {
ranges = new __WEBPACK_IMPORTED_MODULE_0__utils_priority__["a" /* Priority */]({
prioritize: [{
name: 'start',
priority: 'min'
}, {
name: 'end',
priority: 'max'
}],
initialNodes: ranges.slice()
});
var cleanedRanges = [];
if (!ranges.peek()) {
return cleanedRanges;
}
var current = ranges.pop();
var next;
if (!ranges.peek()) {
cleanedRanges.push(current);
}
while (ranges.peek()) {
next = ranges.pop();
// overlapping
// current wholey contains next
// c --------------
// n -----
if (current.start <= next.start && current.end >= next.end) {
next = null;
// re-use current
}
// overlapping
// current left of next
// c ----------
// n -------
else if (current.start < next.start && current.end >= next.start) {
current.end = next.end;
next = null;
// re-use current
}
if (ranges.peek() && next === null) {
continue;
} else if (!ranges.peek() && next !== null) {
cleanedRanges.push(current);
cleanedRanges.push(next);
} else {
cleanedRanges.push(current);
current = next;
}
}
return cleanedRanges;
}
/* unused harmony default export */ var _unused_webpack_default_export = (clean);
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return updateRanges; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return updateBlockRanges; });
function updateRanges(ranges, index, length) {
var updated = [];
var current;
for (var i = 0; i < ranges.length; i++) {
current = ranges[i];
// if adding text
if (length >= 0) {
// previously shrunk range, now to be expanded
if (index === current.start && index === current.end) {
current.end += length;
}
// if index is left of start, change start and end by length
// i
// c --------------
else if (index <= current.start) {
current.start += length;
current.end += length;
}
// if index is right of start, but left of end, change end by length
// i
// c ----------
else if (index >= current.start && index <= current.end) {
current.end += length;
}
// if index is right of end, do nothing
// i
// c ----
else {}
// do nothing
// removing text
} else {
if (index === current.start && index === current.end) {
// do nothing, delete this range
continue;
}
// if index is left of start, change start and end by length
// i
// c --------------
else if (index <= current.start) {
current.start += length;
current.end += length;
}
// if index is right of start, but left of end, change end by length
// i
// c ----------
else if (index >= current.start && index <= current.end) {
// but what if the index change goes leftward past start of c?
if (current.start > index + length && index === current.end) {
// delete range, dont push to update
continue;
} else if (current.start > index + length) {
current.start += length;
if (current.start < 0) {
current.start = 0;
}
}
current.end += length;
}
// if index is right of end, do nothing
// i
// c ----
else {
// but what if the index change goes leftward past end of c?
// if goes past c end, change end by length
if (current.end > index + length) {
current.end = index + length;
}
// but what if the index change goes leftward past start of c?
if (current.start >= index + length) {
// delete range, dont push to update
continue;
}
}
}
updated.push(current);
}
return updated;
}
function updateBlockRanges(blockRanges, index, length) {
var ranges = {};
for (var key in blockRanges) {
ranges[key] = updateRanges(blockRanges[key], index, length);
}
return ranges;
}
/* unused harmony default export */ var _unused_webpack_default_export = (updateRanges);
/***/ }),
/* 2 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Priority; });
function Priority(options) {
var arr = options.initialNodes || [];
this.arr = arr.slice();
this.prioritize = options.prioritize;
if (this.arr.length) {
this.add();
}
}
Priority.prototype.add = function (node) {
var self = this;
if (node !== undefined) {
this.arr.push(node);
}
this.arr.sort(function (a, b) {
var firstCompare = self.compare(a, b, self.prioritize[0].name, self.prioritize[0].priority);
if (0 === firstCompare) {
if (self.prioritize[1]) {
var secondCompare = self.compare(a, b, self.prioritize[1].name, self.prioritize[1].priority);
return secondCompare;
}
}
return firstCompare;
});
};
Priority.prototype.pop = function (node) {
return this.arr.shift();
};
Priority.prototype.peek = function (node) {
return this.arr.length ? this.arr[0] : undefined;
};
Priority.prototype.compare = function (a, b, prop, minOrMax) {
if (minOrMax === 'min') {
if (a[prop] > b[prop]) {
return 1;
} else if (a[prop] < b[prop]) {
return -1;
} else {
return 0;
}
}
if (minOrMax === 'max') {
if (a[prop] > b[prop]) {
return -1;
} else if (a[prop] < b[prop]) {
return 1;
} else {
return 0;
}
}
};
Priority.prototype.toArray = function () {
return this.arr.slice();
};
/* unused harmony default export */ var _unused_webpack_default_export = (Priority);
/***/ }),
/* 3 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return insertText; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__update_ranges__ = __webpack_require__(1);
function insertText(block, index, charsToInsert) {
var rawTextArr = block.rawText.split('');
rawTextArr.splice(index, 0, charsToInsert);
var ranges = {};
for (var key in block.ranges) {
ranges[key] = Object(__WEBPACK_IMPORTED_MODULE_0__update_ranges__["b" /* updateRanges */])(block.ranges[key], index, charsToInsert.length);
}
block.ranges = ranges;
block.rawText = rawTextArr.join('');
return block;
}
/* unused harmony default export */ var _unused_webpack_default_export = (insertText);
/***/ }),
/* 4 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return removeText; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__update_ranges__ = __webpack_require__(1);
function removeText(block, index, length) {
var rawTextArr = block.rawText.split('');
rawTextArr.splice(index - length, length);
var ranges = {};
for (var key in block.ranges) {
ranges[key] = Object(__WEBPACK_IMPORTED_MODULE_0__update_ranges__["b" /* updateRanges */])(block.ranges[key], index, -length);
}
block.ranges = ranges;
block.rawText = rawTextArr.join('');
return block;
}
/* unused harmony default export */ var _unused_webpack_default_export = (removeText);
/***/ }),
/* 5 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Main", function() { return Main; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__render_render_block__ = __webpack_require__(6);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__model_apply_range__ = __webpack_require__(7);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__model_clean__ = __webpack_require__(0);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__handlers_input_handlers__ = __webpack_require__(8);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__handlers_el_handlers__ = __webpack_require__(9);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__cursor_get_cursor__ = __webpack_require__(12);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__cursor_restore_cursor__ = __webpack_require__(13);
function Main(config) {
var self = this;
this.el = config.el;
this.debugEl = config.debugEl;
this.testUiEl = config.testUiEl;
this.data = config.data;
if (this.el) {
this.hiddenInput = document.createElement('textarea');
this.hiddenInput.style.width = this.el.offsetWidth;
document.body.appendChild(this.hiddenInput);
this.el.setAttribute('contentEditable', true);
this.el.style.whiteSpace = 'pre';
this.render();
// EL HANDLERS
this.el.addEventListener('change', function (e) {
if (self.el.offsetWidth !== self.hiddenInput.offsetWidth) {
self.hiddenInput.style.width = self.el.offsetWidth;
}
});
this.el.addEventListener('blur', function (e) {
// cache cursor for when clicking on styling UI
var cursor = self.getCursor();
if (cursor) {
self.cursor = cursor;
}
return true;
});
this.el.addEventListener('keydown', Object(__WEBPACK_IMPORTED_MODULE_4__handlers_el_handlers__["a" /* elKeydownHandler */])(self));
// INPUT HANDLERS
this.hiddenInput.addEventListener('compositionstart', Object(__WEBPACK_IMPORTED_MODULE_3__handlers_input_handlers__["b" /* inputCompositionstartHandler */])(self));
this.hiddenInput.addEventListener('compositionend', Object(__WEBPACK_IMPORTED_MODULE_3__handlers_input_handlers__["a" /* inputCompositionendHandler */])(self));
this.hiddenInput.addEventListener('keydown', Object(__WEBPACK_IMPORTED_MODULE_3__handlers_input_handlers__["d" /* inputKeydownHandler */])(self));
this.hiddenInput.addEventListener('input', Object(__WEBPACK_IMPORTED_MODULE_3__handlers_input_handlers__["c" /* inputInputHandler */])(self));
window._testTome = this;
}
}
Main.prototype.applyRange = function (range) {
this.cursor = this.cursor || this.getCursor();
if (!this.cursor) {
return;
}
var cursor = this.cursor;
var blockStart = cursor.startPath.slice(0, -1).pop();
var blockEnd = cursor.endPath.slice(0, -1).pop();
range.start = range.start || cursor.startPath.slice().pop();
range.end = range.end || cursor.endPath.slice().pop();
for (var i = blockStart; i !== blockEnd + 1; i++) {
this.data.blocks[i].ranges[range.name] = Object(__WEBPACK_IMPORTED_MODULE_1__model_apply_range__["a" /* applyRange */])(this.data.blocks[i].ranges[range.name], {
start: i === blockStart ? range.start : 0,
end: i === blockEnd ? range.end : this.data.blocks[i].rawText.length,
name: range.name,
value: range.value
});
this.data.blocks[i] = Object(__WEBPACK_IMPORTED_MODULE_2__model_clean__["a" /* clean */])(this.data.blocks[i]);
}
this.render();
this.restoreCursor();
};
Main.prototype.render = function () {
if (this.el) {
this.el.innerHTML = Object(__WEBPACK_IMPORTED_MODULE_0__render_render_block__["a" /* renderBlocks */])(this.data);
} else {
return Object(__WEBPACK_IMPORTED_MODULE_0__render_render_block__["a" /* renderBlocks */])(this.data);
}
if (this.debugEl) {
this.debugEl.innerHTML = `<pre>${JSON.stringify(this.data, null, 4)}</pre>`;
}
};
Main.prototype.getCursor = function () {
return __WEBPACK_IMPORTED_MODULE_5__cursor_get_cursor__["a" /* getCursor */].call(this);
};
Main.prototype.restoreCursor = function (cursor) {
__WEBPACK_IMPORTED_MODULE_6__cursor_restore_cursor__["a" /* restoreCursor */].call(this, cursor);
};
Main.prototype.createTestUI = function (UI) {
var self = this;
if (this.testUiEl) {
var uiContainer = document.createElement('DIV');
for (var i = 0; i < UI.length; i++) {
var data = UI[i];
switch (data.el) {
case 'button':
var el = document.createElement('BUTTON');
el.innerHTML = data.label;
el.addEventListener(data.event, data.handler(self));
break;
}
uiContainer.appendChild(el);
}
this.testUiEl.innerHTML = '';
this.testUiEl.appendChild(uiContainer);
}
};
if (window) {
window.Tome = Main;
}
/* harmony default export */ __webpack_exports__["default"] = (Main);
/***/ }),
/* 6 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* unused harmony export renderBlock */
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return renderBlocks; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_priority__ = __webpack_require__(2);
function flattenRanges(obj) {
var arr = [];
for (var key in obj) {
arr.push(obj[key]);
}
return Array.prototype.concat.apply([], arr);
}
// serializes block model to to string representation of HTML
function renderBlock(block) {
var text = '<' + block.blockType.toLowerCase() + '>';
// lowest start index has highest priority
// if two have same start index, the highest max index has priority
var toBeOpened = new __WEBPACK_IMPORTED_MODULE_0__utils_priority__["a" /* Priority */]({
prioritize: [{
name: 'start',
priority: 'min'
}, {
name: 'end',
priority: 'max'
}],
initialNodes: flattenRanges(block.ranges)
});
// lowest end index value has highest priority
var toBeClosed = new __WEBPACK_IMPORTED_MODULE_0__utils_priority__["a" /* Priority */]({
prioritize: [{
name: 'end',
priority: 'min'
}]
});
var char;
for (var i = 0; i < block.rawText.length; i++) {
char = block.rawText[i];
if (toBeOpened.peek() && i === toBeOpened.peek().start) {
while (toBeOpened.peek() && i === toBeOpened.peek().start && toBeOpened.peek().end === toBeOpened.peek().start) {
toBeOpened.pop();
}
text = openTags(text, i, toBeOpened, toBeClosed);
}
// render soft breaks
if (char === '\n') {
// use a zero width character to stand in for \n,
// so that our range code isnt off by one when subing out \n for <br>
text += '<br>&#8203;';
} else {
text += char;
}
if (toBeClosed.peek() && i === toBeClosed.peek().end - 1) {
text = closeTags(text, i, toBeOpened, toBeClosed);
}
}
// if no characters, need a BR for something to click the cursor into
if (!char) {
text += '<br>';
}
return text + '</' + block.blockType.toLowerCase() + '>';
}
// newTag = toBeOpened.pop()
// lastOpen = toBeClosed.peek()
// if newTag.end > lastOpen.end,
// you need to close lastOpen, and re-open lastOpen after newTag
function openTags(text, i, toBeOpened, toBeClosed) {
if (toBeOpened.peek() && i === toBeOpened.peek().start) {
var range = toBeOpened.pop();
if (toBeClosed.peek() && range.end > toBeClosed.peek().end) {
// NEED TO CLOSE ALL toBeCLosed,
// and REOPEN by closing latest priority (include the new range in this priority)
for (var j = 0; j < toBeClosed.toArray().length; j++) {
text = closeSpan(text, {});
}
var reOpen = new __WEBPACK_IMPORTED_MODULE_0__utils_priority__["a" /* Priority */]({
prioritize: [{
name: 'end',
priority: 'max'
}],
initialNodes: toBeClosed.toArray().concat(range)
});
while (reOpen.peek()) {
text = openSpan(text, reOpen.pop());
}
} else {
text = openSpan(text, range);
}
toBeClosed.add(range);
text = openTags(text, i, toBeOpened, toBeClosed);
}
return text;
}
function closeTags(text, i, toBeOpened, toBeClosed) {
if (toBeClosed.peek() && i === toBeClosed.peek().end - 1) {
text = closeSpan(text, toBeClosed.pop());
text = closeTags(text, i, toBeOpened, toBeClosed);
}
return text;
}
function openSpan(text, style) {
return text + '<span style="' + toCSSName(style.name) + ': ' + (style.value || '') + (style.unit || '') + ';">';
}
function closeSpan(text, style) {
return text += '</span>';
}
function toCSSName(str) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
function renderBlocks(data) {
var text = '';
for (var block in data.blocks) {
text += renderBlock(data.blocks[block]);
}
return text;
}
/* unused harmony default export */ var _unused_webpack_default_export = (renderBlock);
/***/ }),
/* 7 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return applyRange; });
function applyRange(ranges = [], newRange) {
// need to iterate the entire ranges array
// shrink ALL conflicting ranges (same type, different value)
// then insert the newRange at the very end
var deconflictedRanges = [];
var current;
for (var i = 0; i < ranges.length; i++) {
current = ranges[i];
// let cleanRanges deal with deduping same value ranges
if (current.value === newRange.value) {
deconflictedRanges.push(current);
continue;
}
// different value, need to check
// overlapping
// current wholey contains new
// c --------------
// n -----
if (current.start <= newRange.start && current.end >= newRange.end) {
// need to split current into two ranges
// c --- n ---- c ----
// current1 current start with new's start as end
// current2 new end current end
deconflictedRanges.push({
name: current.name,
value: current.value,
start: current.start,
end: newRange.start
});
deconflictedRanges.push({
name: current.name,
value: current.value,
start: newRange.end,
end: current.end
});
}
// overlapping
// current contained wholey inside new
// n --------------
// c ----
else if (newRange.start <= current.start && newRange.end >= current.end) {
continue;
}
// overlapping
// current left of new
// c ----------
// n -------
else if (current.start < newRange.start && current.end >= newRange.start) {
// need to subtract from current's end property
// c --- n ------
current.end = newRange.start;
deconflictedRanges.push(current);
}
// overlapping
// current right of new
// n --------
// c -------
else if (newRange.start < current.start && newRange.end >= current.start) {
// need to add to current's start property
// n ------- c---
current.start = newRange.end;
deconflictedRanges.push(current);
} else {
deconflictedRanges.push(current);
}
}
deconflictedRanges.push(newRange);
return deconflictedRanges;
}
/* unused harmony default export */ var _unused_webpack_default_export = (applyRange);
/***/ }),
/* 8 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return inputInputHandler; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "d", function() { return inputKeydownHandler; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return inputCompositionstartHandler; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return inputCompositionendHandler; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__model_clean__ = __webpack_require__(0);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__model_insert_text__ = __webpack_require__(3);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__model_remove_text__ = __webpack_require__(4);
function inputInputHandler(self) {
return function (e) {
var input = this;
var blockStart = self.cursor.startPath.slice(0, -1).pop();
var cursorStart = self.cursor.startPath.slice().pop();
var cursorEnd = self.cursor.endPath.slice().pop();
// text composition
if (self.composition !== undefined) {
var chars = input.value.slice(self.composition.index, input.selectionEnd);
if (self.composition.state === 'start') {
self.data.blocks[blockStart] = Object(__WEBPACK_IMPORTED_MODULE_0__model_clean__["a" /* clean */])(Object(__WEBPACK_IMPORTED_MODULE_1__model_insert_text__["a" /* insertText */])(self.data.blocks[blockStart], self.composition.index, chars));
self.render();
self.composition.state = 'composing';
} else if (self.composition.state === 'composing') {
self.data.blocks[blockStart] = Object(__WEBPACK_IMPORTED_MODULE_0__model_clean__["a" /* clean */])(Object(__WEBPACK_IMPORTED_MODULE_2__model_remove_text__["a" /* removeText */])(self.data.blocks[blockStart], self.composition.index + 1, chars.length));
self.data.blocks[blockStart] = Object(__WEBPACK_IMPORTED_MODULE_0__model_clean__["a" /* clean */])(Object(__WEBPACK_IMPORTED_MODULE_1__model_insert_text__["a" /* insertText */])(self.data.blocks[blockStart], self.composition.index, chars));
self.render();
} else if (self.composition.state === 'end') {
self.data.blocks[blockStart] = Object(__WEBPACK_IMPORTED_MODULE_0__model_clean__["a" /* clean */])(Object(__WEBPACK_IMPORTED_MODULE_2__model_remove_text__["a" /* removeText */])(self.data.blocks[blockStart], self.composition.index + 1, chars.length));
self.data.blocks[blockStart] = Object(__WEBPACK_IMPORTED_MODULE_0__model_clean__["a" /* clean */])(Object(__WEBPACK_IMPORTED_MODULE_1__model_insert_text__["a" /* insertText */])(self.data.blocks[blockStart], self.composition.index, chars));
self.cursor.startPath.pop();
self.cursor.startPath.push(input.selectionStart);
// self.cursor.start = input.selectionStart;
self.cursor.endPath.pop();
self.cursor.endPath.push(input.selectionEnd);
// self.cursor.end = input.selectionEnd;
self.render();
self.restoreCursor();
self.composition = undefined;
}
return;
}
// regular text insertion
if (cursorStart === cursorEnd && input.selectionStart > cursorStart) {
var chars = input.value.slice(cursorStart, input.selectionStart);
self.data.blocks[blockStart] = Object(__WEBPACK_IMPORTED_MODULE_0__model_clean__["a" /* clean */])(Object(__WEBPACK_IMPORTED_MODULE_1__model_insert_text__["a" /* insertText */])(self.data.blocks[blockStart], cursorStart, chars));
self.cursor.startPath.pop();
self.cursor.startPath.push(input.selectionStart);
self.cursor.endPath.pop();
self.cursor.endPath.push(input.selectionStart);
}
self.render();
self.restoreCursor();
};
}
function inputKeydownHandler(self) {
return function (e) {
// prevent starting any new compositions when in the input
if (e.altKey) {
e.preventDefault();
e.stopPropagation();
return false;
}
};
}
// http://blog.evanyou.me/2014/01/03/composition-event/
function inputCompositionstartHandler(self) {
return function (e) {
var input = this;
self.composition = {
state: 'start',
index: input.selectionStart
};
};
}
function inputCompositionendHandler(self) {
return function (e) {
self.composition = {
state: 'end',
index: self.composition.index
};
};
}
/***/ }),
/* 9 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return elKeydownHandler; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__model_clean__ = __webpack_require__(0);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__model_insert_text__ = __webpack_require__(3);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__model_remove_text__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__model_merge_blocks__ = __webpack_require__(10);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__model_split_block__ = __webpack_require__(11);
function elKeydownHandler(self) {
return function (e) {
// http://www.quirksmode.org/js/keys.html
// hot keys
if (e.metaKey || e.ctrlKey) {
switch (e.keyCode) {
case 73:
// i
e.preventDefault();
e.stopPropagation();
self.applyRange({ name: 'fontStyle', value: 'italic' });
return false;
case 66:
// b
e.preventDefault();
e.stopPropagation();
self.applyRange({ name: 'fontWeight', value: 700 });
return false;
case 85:
// u
e.preventDefault();
e.stopPropagation();
self.applyRange({ name: 'textDecoration', value: 'underline' });
return false;
}
}
switch (e.keyCode) {
// ARROWS
case 37: // left
case 39: // right
case 38: // up
case 40:
// down
break;
case 13:
// enter
e.preventDefault();
e.stopPropagation();
if (e.shiftKey) {
interceptCrossBlockChanges.call(self);
handleShiftEnter.call(self);
} else {
interceptCrossBlockChanges.call(self);
handleEnter.call(self);
}
return false;
case 27: // escape
case 16: // shift
case 18: // alt / option
case 91: // command/meta
case 93:
// command/meta
break;
// case 9: // tab
// break;
// case ??: // fn ?
// fn keys?
case 46:
// backspace
e.preventDefault();
e.stopPropagation();
handleDelete.call(self, 46);
return false;
case 8:
// delete
e.preventDefault();
e.stopPropagation();
handleDelete.call(self, 8);
return false;
// FOCUS THE HIDDEN INPUT BOX
default:
interceptCrossBlockChanges.call(self);
focusInput.call(self);
}
return true;
};
}
function focusInput() {
var cursor = this.getCursor();
if (!cursor) {
return;
} else {
this.cursor = cursor;
}
var start = cursor.startPath.slice().pop();
var end = cursor.endPath.slice().pop();
var block = cursor.startPath.slice(0, -1).pop();
this.hiddenInput.focus();
this.hiddenInput.value = this.data.blocks[block].rawText;
this.hiddenInput.setSelectionRange(start, end);
}
function interceptCrossBlockChanges() {
var cursor = this.getCursor();
if (!cursor) {
return;
} else {
this.cursor = cursor;
}
var charStart = cursor.startPath.slice().pop();
var charEnd = cursor.endPath.slice().pop();
var blockStart = cursor.startPath.slice(0, -1).pop();
var blockEnd = cursor.endPath.slice(0, -1).pop();
if (blockStart !== blockEnd) {
// delete partially text on first outside block
var deleteStart = this.data.blocks[blockStart].rawText.length;
var startLength = deleteStart - charStart;
this.data.blocks[blockStart] = Object(__WEBPACK_IMPORTED_MODULE_0__model_clean__["a" /* clean */])(Object(__WEBPACK_IMPORTED_MODULE_2__model_remove_text__["a" /* removeText */])(this.data.blocks[blockStart], deleteStart, startLength));
// delete partially text on last outside block
this.data.blocks[blockEnd] = Object(__WEBPACK_IMPORTED_MODULE_0__model_clean__["a" /* clean */])(Object(__WEBPACK_IMPORTED_MODULE_2__model_remove_text__["a" /* removeText */])(this.data.blocks[blockEnd], charEnd, charEnd));
// remove entirely any blocks in the middle
this.data.blocks.splice(blockStart + 1, blockEnd - blockStart - 1);
// merge startBlock and endBlock
this.data.blocks = Object(__WEBPACK_IMPORTED_MODULE_3__model_merge_blocks__["a" /* mergeBlocks */])(this.data.blocks, blockStart, blockStart + 1);
this.cursor.endPath.pop();
this.cursor.endPath.pop();
this.cursor.endPath.push(blockStart);
this.cursor.endPath.push(charStart);
this.render();
this.restoreCursor();
return true;
} else if (charStart !== charEnd) {
this.data.blocks[blockStart] = Object(__WEBPACK_IMPORTED_MODULE_0__model_clean__["a" /* clean */])(Object(__WEBPACK_IMPORTED_MODULE_2__model_remove_text__["a" /* removeText */])(this.data.blocks[blockStart], charEnd, charEnd - charStart));
this.cursor.endPath.pop();
this.cursor.endPath.push(charStart);
this.render();
this.restoreCursor();
return true;
}
return false;
}
function handleDelete(keyCode) {
var handled = interceptCrossBlockChanges.call(this);
if (handled) {
return;
}
var cursor = this.getCursor();
if (!cursor) {
return;
} else {
this.cursor = cursor;
}
// TODO
// problem with the rendering engine, when start === end
// color 0s0e is sometimes making spans that wrap 3 or 4 chars
// debug in renderBlock
// more bugs when say color is 9 9 making whole 5 chars after green
var charStart = cursor.startPath.slice().pop();
var charEnd = cursor.endPath.slice().pop();
var blockStart = cursor.startPath.slice(0, -1).pop();
// var blockEnd = cursor.endPath.slice(0, -1).pop();
var blockLength = this.data.blocks[blockStart].rawText.length;
/* first char of paragraph */
if (keyCode === 8 && charStart === 0) {
blockLength = this.data.blocks[blockStart - 1].rawText.length;
this.data.blocks = Object(__WEBPACK_IMPORTED_MODULE_3__model_merge_blocks__["a" /* mergeBlocks */])(this.data.blocks, blockStart - 1, blockStart);
this.cursor.startPath.pop();
this.cursor.startPath.pop();
this.cursor.startPath.push(blockStart - 1);
this.cursor.startPath.push(blockLength);
this.cursor.endPath.pop();
this.cursor.endPath.pop();
this.cursor.endPath.push(blockStart - 1);
this.cursor.endPath.push(blockLength);
/* last char of paragraph */
} else if (keyCode === 46 && charEnd === blockLength) {
this.data.blocks = Object(__WEBPACK_IMPORTED_MODULE_3__model_merge_blocks__["a" /* mergeBlocks */])(this.data.blocks, blockStart, blockStart + 1);
this.cursor.startPath.pop();
this.cursor.startPath.pop();
this.cursor.startPath.push(blockStart);
this.cursor.startPath.push(blockLength);
this.cursor.endPath.pop();
this.cursor.endPath.pop();
this.cursor.endPath.push(blockStart);
this.cursor.endPath.push(blockLength);
/* regular delete */
} else if (keyCode === 8) {
this.data.blocks[blockStart] = Object(__WEBPACK_IMPORTED_MODULE_0__model_clean__["a" /* clean */])(Object(__WEBPACK_IMPORTED_MODULE_2__model_remove_text__["a" /* removeText */])(this.data.blocks[blockStart], charEnd, 1));
this.cursor.startPath.pop();
this.cursor.startPath.push(charStart - 1);
this.cursor.endPath.pop();
this.cursor.endPath.push(charStart - 1);
/* regular backspace */
} else if (keyCode === 46) {
this.data.blocks[blockStart] = Object(__WEBPACK_IMPORTED_MODULE_0__model_clean__["a" /* clean */])(Object(__WEBPACK_IMPORTED_MODULE_2__model_remove_text__["a" /* removeText */])(this.data.blocks[blockStart], charEnd + 1, 1));
this.cursor.endPath.pop();
this.cursor.endPath.push(charStart);
}
this.render();
this.restoreCursor();
}
function handleEnter() {
var cursor = this.getCursor();
if (!cursor) {
return;
} else {
this.cursor = cursor;
}
var charStart = cursor.startPath.slice().pop();
var blockStart = cursor.startPath.slice(0, -1).pop();
// splitBlock
this.data.blocks = Object(__WEBPACK_IMPORTED_MODULE_4__model_split_block__["a" /* splitBlock */])(this.data.blocks, blockStart, charStart);
this.cursor.startPath.pop();
this.cursor.startPath.pop();
this.cursor.startPath.push(blockStart + 1);
this.cursor.startPath.push(0);
this.cursor.endPath.pop();
this.cursor.endPath.pop();
this.cursor.endPath.push(blockStart + 1);
this.cursor.endPath.push(0);
this.render();
this.restoreCursor();
}
function handleShiftEnter() {
var cursor = this.getCursor();
if (!cursor) {
return;
} else {
this.cursor = cursor;
}
var charStart = cursor.startPath.slice().pop();
var blockStart = cursor.startPath.slice(0, -1).pop();
this.data.blocks[blockStart] = Object(__WEBPACK_IMPORTED_MODULE_0__model_clean__["a" /* clean */])(Object(__WEBPACK_IMPORTED_MODULE_1__model_insert_text__["a" /* insertText */])(this.data.blocks[blockStart], charStart, '\n'));
this.cursor.startPath.pop();
this.cursor.endPath.pop();
this.cursor.startPath.push(charStart + 1);
this.cursor.endPath.push(charStart + 1);
this.render();
this.restoreCursor();
}
/***/ }),
/* 10 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return mergeBlocks; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__update_ranges__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__clean__ = __webpack_require__(0);
function mergeBlocks(blocks, blockIndexA, blockIndexB) {
var blockA = blocks[blockIndexA];
var blockB = blocks[blockIndexB];
var blockARanges = JSON.parse(JSON.stringify(blockA.ranges));
var blockBRanges = Object(__WEBPACK_IMPORTED_MODULE_0__update_ranges__["a" /* updateBlockRanges */])(JSON.parse(JSON.stringify(blockB.ranges)), 0, blockA.rawText.length);
// merge blockBranges into blockAranges
var newRanges = Object.keys(blockBRanges).reduce(function (blockARanges, key) {
var rangeArray = blockARanges[key] || [];
blockARanges[key] = rangeArray.concat(blockBRanges[key]);
return blockARanges;
}, blockARanges || {});
var newBlock = Object(__WEBPACK_IMPORTED_MODULE_1__clean__["a" /* clean */])({
blockType: blockA.blockType,
rawText: blockA.rawText + blockB.rawText,
ranges: newRanges
});
blocks.splice(blockIndexA, 2, newBlock);
return blocks;
}
/* unused harmony default export */ var _unused_webpack_default_export = (mergeBlocks);
/***/ }),
/* 11 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return splitBlock; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__update_ranges__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__clean__ = __webpack_require__(0);
function splitBlock(blocks, blockIndex, charIndex) {
var blockToSplit = blocks[blockIndex];
var oldBlockText = blockToSplit.rawText.slice(0, charIndex);
var newBlockText = blockToSplit.rawText.slice(charIndex);
var oldBlockRanges = Object(__WEBPACK_IMPORTED_MODULE_0__update_ranges__["a" /* updateBlockRanges */])(JSON.parse(JSON.stringify(blockToSplit.ranges)), blockToSplit.rawText.length, -newBlockText.length);
var newBlockRanges = Object(__WEBPACK_IMPORTED_MODULE_0__update_ranges__["a" /* updateBlockRanges */])(JSON.parse(JSON.stringify(blockToSplit.ranges)), charIndex, -charIndex);
var oldBlock = Object(__WEBPACK_IMPORTED_MODULE_1__clean__["a" /* clean */])({
blockType: blockToSplit.blockType,
rawText: oldBlockText,
ranges: oldBlockRanges
});
var newBlock = Object(__WEBPACK_IMPORTED_MODULE_1__clean__["a" /* clean */])({
blockType: blockToSplit.blockType,
rawText: newBlockText,
ranges: newBlockRanges
});
blocks.splice(blockIndex, 1, oldBlock, newBlock);
return blocks;
}
/* unused harmony default export */ var _unused_webpack_default_export = (splitBlock);
/***/ }),
/* 12 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getCursor; });
function getCursor() {
if (!window.getSelection().rangeCount) {
return;
}
var nativeRange = window.getSelection().getRangeAt(0);
var deepestStartBlock = getTarget(nativeRange.startContainer, 'P, LI, UL, OL, H1, H2, H3, H4, H5');
var startPath = [];
// get the character index of the block
startPath.unshift(countFromLeft(deepestStartBlock, nativeRange.startContainer, nativeRange.startOffset));
// prepend the block paths
getFullBlockPath(startPath, deepestStartBlock);
var deepestEndBlock = getTarget(nativeRange.endContainer, 'P, LI, UL, OL, H1, H2, H3, H4, H5');
var endPath = [];
// get the character index of the block
endPath.unshift(countFromLeft(deepestEndBlock, nativeRange.endContainer, nativeRange.endOffset));
// prepend the block paths
getFullBlockPath(endPath, deepestEndBlock);
return {
startPath: startPath,
endPath: endPath,
nativeRange: nativeRange
};
// // recurse upward until parent is contentEditable
// // get index of which block you are
// var startBlock = getTargetParentChild(nativeRange.startContainer, '[data-tome]');
// var blockStart = indexOf( startBlock.parentElement.childNodes, startBlock );
// // recurse upward until parent is contentEditable
// // get index of which block you are
// var endBlock = getTargetParentChild(nativeRange.endContainer, '[data-tome]');
// var blockEnd = indexOf( endBlock.parentElement.childNodes, endBlock );
// // walk left to beginning of block, count chars
// var start = countFromLeft(startBlock, nativeRange.startContainer, nativeRange.startOffset);
// // walk right to end of block, count chars
// var end = countFromLeft(endBlock, nativeRange.endContainer, nativeRange.endOffset);
// return {
// blockStart: blockStart,
// blockEnd: blockEnd,
// start: start,
// end: end,
// nativeRange: nativeRange
// };
}
function getFullBlockPath(arr, block) {
if (matchesSelector(block, '[data-tome]')) {
return;
}
if (matchesSelector(block, 'P, LI, UL, OL, H1, H2, H3, H4, H5')) {
var i = indexOf(block.parentElement.childNodes, block);
arr.unshift(i);
}
if (block.parentElement) {
getFullBlockPath(arr, block.parentElement);
}
}
function countFromLeft(block, target, targetOffset) {
// if target not a text node, recurse down???
var textNodes = collectTextNodes(block, []);
var count = 0;
for (var i = 0; i < textNodes.length; i++) {
if (textNodes[i] === target) {
count += targetOffset;
break;
}
count += textNodes[i].textContent.length;
}
return count;
}
// function countFromRight(block, target, targetOffset){
// // if target not a text node, recurse down???
// var textNodes = collectTextNodes(block, []);
// var count = 0;
// for (var i= textNodes.length - 1; i >= 0; i--){
// if (textNodes[i] === target){
// count += targetOffset;
// break;
// }
// count += textNodes[i].textContent.length;
// }
// return count;
// }
function collectTextNodes(element, texts) {
for (var child = element.firstChild; child !== null; child = child.nextSibling) {
if (child.nodeType === 3) {
texts.push(child);
} else if (child.nodeType === 1) {
collectTextNodes(child, texts);
}
}
return texts;
}
// function getTargetParentChild(el, selector){
// if (matchesSelector(el.parentElement, selector)){
// return el;
// } else {
// return getTargetParentChild(el.parentElement, selector);
// }
// }
function getTarget(el, selector) {
if (matchesSelector(el, selector)) {
return el;
} else {
return getTarget(el.parentElement, selector);
}
}
function matchesSelector(element, selector) {
var matches = (element.document || element.ownerDocument).querySelectorAll(selector);
var i = 0;
while (matches[i] && matches[i] !== element) {
i++;
}
return matches[i] ? true : false;
}
function indexOf(arr, node) {
return Array.prototype.indexOf.call(arr, node);
}
/* unused harmony default export */ var _unused_webpack_default_export = (getCursor);
/***/ }),
/* 13 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return restoreCursor; });
function restoreCursor(cursor) {
cursor = cursor || this.cursor;
var nativeSelection = window.getSelection();
var deepestStartBlock = getDeepestBlockFromPath(this.el, cursor.startPath);
var deepestEndBlock = getDeepestBlockFromPath(this.el, cursor.endPath);
var starting = getTextNodeFromCount(deepestStartBlock, last(cursor.startPath));
var ending = getTextNodeFromCount(deepestEndBlock, last(cursor.endPath));
// var startBlock = getBlockFromIndex(this.el, cursor.blockStart);
// var endBlock = getBlockFromIndex(this.el, cursor.blockEnd);
// var starting = getTextNodeFromCount(startBlock, cursor.start);
// var ending = getTextNodeFromCount(endBlock, cursor.end);
var range = document.createRange();
range.setStart(starting.node || deepestStartBlock, starting.offset || 0);
range.setEnd(ending.node || deepestEndBlock, ending.offset || 0);
nativeSelection.removeAllRanges();
nativeSelection.addRange(range);
}
function last(arr) {
return arr.length ? arr[arr.length - 1] : undefined;
}
function getDeepestBlockFromPath(block, path) {
var drill = path.slice(0, -1);
var current = block;
while (drill.length) {
current = current.childNodes[drill.shift()];
}
return current;
}
// function getBlockFromIndex(editableRoot, index){
// return editableRoot.children[index];
// }
function getTextNodeFromCount(block, targetCount) {
var offset;
var node;
var textNodes = collectTextNodes(block, []);
var count = 0;
for (var i = 0; i < textNodes.length; i++) {
if (textNodes[i].textContent.length + count >= targetCount) {
node = textNodes[i];
offset = targetCount - count;
break;
}
count += textNodes[i].textContent.length;
}
return {
node: node,
offset: offset
};
}
function collectTextNodes(element, texts) {
for (var child = element.firstChild; child !== null; child = child.nextSibling) {
if (child.nodeType === 3) {
texts.push(child);
} else if (child.nodeType === 1) {
collectTextNodes(child, texts);
}
}
return texts;
}
/* unused harmony default export */ var _unused_webpack_default_export = (restoreCursor);
/***/ })
/******/ ]);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment