-$(function() {
- $('div[contenteditable]').on('input', function(event) {
- if (event.originalEvent.inputType == "insertParagraph") {
+(function(window, document, undefined) {
+ var Phylly = {
+ BLOCKS: [
+ {name: 'code', tag: 'PRE', klass: 'screen'},
+ {name: 'figure', tag: 'DIV', subtag: true, klass: 'figure'},
+ {name: 'note', tag: 'DIV', subtag: true, klass: 'note'},
+ ],
+ input_event: function(event) {
+ if (event.originalEvent.inputType != "insertParagraph") return true;
var sel = document.getSelection();
var anchorNode = sel.anchorNode;
var prev_p = sel.anchorNode.previousSibling;
+ if (! prev_p) return;
if (prev_p.tagName != 'P') {
prev_p = $(prev_p).parents('p')[0];
}
title.textContent = title.textContent.slice(4);
prev_p.replaceWith(title);
}
+ return true;
+ },
+
+ init: function() {
+ $(document).on('selectionchange', function(event) {
+ if ($('input[name=link-target].shown').length) {
+ return;
+ }
+ var sel = window.getSelection();
+ if ($(sel.anchorNode).parents('div[contenteditable]').length && sel.toString()) {
+ show_style_popup(sel);
+ } else if (style_popup) {
+ $(style_popup).hide();
+ }
+ });
+ },
+
+ bind_events: function(elem) {
+ $(elem).on('input', Phylly.input_event);
+ $(elem).on('keyup click', update_block_style_popup);
+ },
+
+ }
+ window.Phylly = Phylly;
+
+ function get_contenteditable_subnode(node) {
+ if (node === null) return null;
+ if (node.parentNode.contentEditable === 'true') return node;
+ return get_contenteditable_subnode(node.parentNode);
+ }
+ function get_active_block(node) {
+ var main_node = get_contenteditable_subnode(node);
+ if (main_node === null) return null;
+ for (const block of Phylly.BLOCKS) {
+ if (main_node.tagName === block.tag && main_node.classList.contains(block.klass))
+ return block;
}
- return true;
- });
+ return null;
+ }
- $('#save').on('click', function() {
- var text = $('div[contenteditable]')[0].innerHTML;
- $.post('api-save/', {text: text}).fail(function() {
- $('#save').css('background', 'red');
- });
- return false;
- });
+ var block_style_popup = null;
+ function block_style() {
+ var sel = window.getSelection();
+ var current_anchor = sel.anchorNode;
+ if (this.classList.contains('on')) { // toggle off
+ if (this.action_block.subtag) {
+ // unwrap
+ var main_node = get_contenteditable_subnode(current_anchor);
+ $(current_anchor).detach().insertAfter(main_node);
+ } else {
+ document.execCommand('formatBlock', false, 'p');
+ current_anchor = sel.anchorNode;
+ }
+ } else {
+ action = this.action_block.subtag || this.action_block.tag;
+ if (this.action_block.subtag) {
+ // enclose current tag into a new parent;
+ var new_parent = document.createElement(this.action_block.tag);
+ new_parent.className = this.action_block.klass;
+ $(current_anchor).wrap(new_parent);
+ } else {
+ document.execCommand('formatBlock', false, this.action_block.tag);
+ sel.anchorNode.className = this.action_block.klass;
+ current_anchor = sel.anchorNode;
+ }
+ }
+ var range = document.createRange();
+ range.setStart(current_anchor, 0);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ update_block_style_popup();
+ }
+ function update_block_style_popup() {
+ var sel = window.getSelection();
+ if (! ((sel.anchorNode instanceof Element && (sel.anchorOffset == 0 && sel.isCollapsed)) || get_active_block(sel.anchorNode))) {
+ if (block_style_popup) {
+ $(block_style_popup).hide();
+ }
+ return true;
+ }
+ if (block_style_popup === null) {
+ block_style_popup = $('<div class="block-style-popup"></div>');
+ for (const block of Phylly.BLOCKS) {
+ var button = document.createElement('button');
+ button.action_block = block;
+ button.dataset.action = block.name;
+ button.textContent = block.name;
+ block_style_popup.append(button);
+ }
+ block_style_popup.hide();
+ block_style_popup.insertAfter(document.body);
+ block_style_popup.find('button').on('click', block_style);
+ }
+ block_style_popup.css('position', 'absolute');
+ var block = get_active_block(sel.anchorNode);
+ block_style_popup.find('button').removeClass('on');
+ if (block) {
+ block_style_popup.find('[data-action=' + block.name + ']').addClass('on');
+ block_style_popup.addClass('selected');
+ } else {
+ block_style_popup.removeClass('selected');
+ }
+ var anchor = get_contenteditable_subnode(sel.anchorNode);
+ var pos = $(anchor).offset();
+ block_style_popup.css('top', pos.top - 33);
+ block_style_popup.css('left', pos.left);
+ block_style_popup.show();
+ return true;
+ }
var style_popup = null;
function update_style() {
action = 'insertHTML';
param = $('<code></code>', {text: window.getSelection().toString()})[0].outerHTML;
}
+ if (action == 'createLink') {
+ var sel = window.getSelection();
+ var $input = $('input[name=link-target]');
+ $input[0]._range = sel.getRangeAt(0);
+ if (sel.anchorNode instanceof Element) {
+ var elem = sel.anchorNode.childNodes[sel.anchorOffset];
+ if (elem.tagName == 'A') {
+ $input.val(elem.href);
+ }
+ }
+ $input.addClass('shown');
+ $input.focus();
+ return;
+ }
document.execCommand(action, false, param);
}
+ function validate_link(ev) {
+ var charCode = typeof ev.which == "number" ? ev.which : ev.keyCode;
+ if (ev.key == "Enter") {
+ var $input = $(this);
+ var range = this._range;
+ var url = $input.val();
+ $input.removeClass('shown');
+ var sel = window.getSelection();
+ sel.addRange(this._range);
+ if (url) {
+ document.execCommand('createLink', false, url);
+ } else {
+ document.execCommand('unlink', false, null);
+ }
+ sel.empty();
+ $input.val('');
+ }
+ }
+ function focusout_link(ev) {
+ var $input = $(this);
+ $input.removeClass('shown');
+ var range = this._range;
+ var sel = window.getSelection();
+ sel.addRange(this._range);
+ }
function show_style_popup(sel) {
if (style_popup === null) {
- style_popup = $('<div id="style-popup">' +
+ style_popup = $('<div class="inline-style-popup">' +
'<button data-action="italic"><i>i</i></button>' +
'<button data-action="bold"><b>b</b></button>' +
- '<button data-action="code">#</button>' +
+ '<button data-action="code"><></button>' +
'<button data-action="removeFormat">×</button>' +
+ '<button data-action="createLink">a</button>' +
+ '<input name="link-target"/>' +
'</div>');
style_popup.hide();
- style_popup.insertAfter($('div[contenteditable]'));
+ style_popup.insertAfter(document.body);
style_popup.find('button').on('click', update_style);
+ style_popup.find('[name=link-target]').on('keypress', validate_link).on('focusout', focusout_link);
}
style_popup.css('position', 'absolute');
var pos = sel.getRangeAt(0).getClientRects()[0];
- style_popup.css('top', pos.top + window.scrollY - $('main').offset().top - 33);
- style_popup.css('left', pos.left + window.scrollX - $('main').offset().left);
+ style_popup.css('top', pos.top + window.scrollY - 33);
+ style_popup.css('left', pos.left + window.scrollX);
style_popup.show();
};
- $(document).on('selectionchange', function(event) {
- var sel = window.getSelection();
- if ($(sel.anchorNode).parents('div[contenteditable]').length && sel.toString()) {
- show_style_popup(sel);
- } else if (style_popup) {
- $(style_popup).hide();
- }
+}(window, document));
+
+$(function() {
+ Phylly.init(),
+ $('div[contenteditable]').each(function(i, elem) {Phylly.bind_events(elem)});
+ $('#save').on('click', function() {
+ var text = $('div[contenteditable]')[0].innerHTML;
+ var csrf = $('[name=csrfmiddlewaretoken]').val();
+ $.post('api-save/',
+ { text: text, csrfmiddlewaretoken: csrf}
+ ).fail(function() {
+ $('#save').css('background', 'red');
+ });
+ return false;
});
});