diff --git a/app/assets/javascripts/app/lib/base/jquery.contenteditable.js b/app/assets/javascripts/app/lib/base/jquery.contenteditable.js index 85618ba36..05b6d5567 100644 --- a/app/assets/javascripts/app/lib/base/jquery.contenteditable.js +++ b/app/assets/javascripts/app/lib/base/jquery.contenteditable.js @@ -66,402 +66,408 @@ this.browserMagicKey = App.Browser.magicKey() this.browserHotkeys = App.Browser.hotkeys() - this.init(); + this.init() } Plugin.prototype.init = function () { - var _this = this + this.bindEvents() + this.$element.enableObjectResizingShim() + } - this.toggleBlock = function(tag) { - sel = window.getSelection() - node = $(sel.anchorNode) - if (node.is(tag) || node.parent().is(tag) || node.parent().parent().is(tag)) { - document.execCommand('formatBlock', false, 'div') - //document.execCommand('RemoveFormat') + Plugin.prototype.bindEvents = function () { + this.$element.on('keydown', this.onKeydown.bind(this)) + this.$element.on('paste', this.onPaste.bind(this)) + this.$element.on('dragover', this.onDragover.bind(this)) + this.$element.on('drop', this.onDrop.bind(this)) + } + + Plugin.prototype.toggleBlock = function(tag) { + sel = window.getSelection() + node = $(sel.anchorNode) + if (node.is(tag) || node.parent().is(tag) || node.parent().parent().is(tag)) { + document.execCommand('formatBlock', false, 'div') + //document.execCommand('RemoveFormat') + } + else { + document.execCommand('formatBlock', false, tag) + } + } + + Plugin.prototype.onKeydown = function (e) { + this.log('keydown', e.keyCode) + if (this.preventInput) { + this.log('preventInput', this.preventInput) + return + } + + // strap the return key being pressed + if (e.keyCode === 13) { + + // disbale multi line + if (!this.options.multiline) { + e.preventDefault() + return } - else { - document.execCommand('formatBlock', false, tag) + + // break
after enter on empty line + sel = window.getSelection() + if (sel) { + node = $(sel.anchorNode) + if (node && node.parent() && node.parent().is('blockquote')) { + e.preventDefault() + document.execCommand('Insertparagraph') + document.execCommand('Outdent') + return + } + } + + // behavior to enter new line on alt+enter + // on alt + enter not realy newline is fired, to make + // it compat. to other systems, do it here + if (!e.shiftKey && e.altKey && !e.ctrlKey && !e.metaKey) { + e.preventDefault() + this.paste('
') + return } } - // handle enter - this.$element.on('keydown', function (e) { - _this.log('keydown', e.keyCode) - if (_this.preventInput) { - this.log('preventInput', _this.preventInput) - return + // on zammad magicKey + i/b/u/s + // hotkeys + u -> Toggles the current selection between underlined and not underlined + // hotkeys + b -> Toggles the current selection between bold and non-bold + // hotkeys + i -> Toggles the current selection between italic and non-italic + // hotkeys + v -> Toggles the current selection between strike and non-strike + // hotkeys + f -> Removes the formatting tags from the current selection + // hotkeys + y -> Removes the formatting from while textarea + // hotkeys + z -> Inserts a Horizontal Rule + // hotkeys + l -> Toggles the text selection between an unordered list and a normal block + // hotkeys + k -> Toggles the text selection between an ordered list and a normal block + // hotkeys + o -> Draws a line through the middle of the current selection + // hotkeys + w -> Removes any hyperlink from the current selection + var richtTextControl = false + if (this.browserMagicKey == 'cmd') { + if (!e.altKey && !e.ctrlKey && e.metaKey) { + richtTextControl = true } - - // strap the return key being pressed - if (e.keyCode === 13) { - - // disbale multi line - if (!_this.options.multiline) { - e.preventDefault() - return - } - - // breakafter enter on empty line - sel = window.getSelection() - if (sel) { - node = $(sel.anchorNode) - if (node && node.parent() && node.parent().is('blockquote')) { - e.preventDefault() - document.execCommand('Insertparagraph') - document.execCommand('Outdent') - return - } - } - - // behavior to enter new line on alt+enter - // on alt + enter not realy newline is fired, to make - // it compat. to other systems, do it here - if (!e.shiftKey && e.altKey && !e.ctrlKey && !e.metaKey) { - e.preventDefault() - _this.paste('
') - return - } + } + else { + if (!e.altKey && e.ctrlKey && !e.metaKey) { + richtTextControl = true } - - // on zammad magicKey + i/b/u/s - // hotkeys + u -> Toggles the current selection between underlined and not underlined - // hotkeys + b -> Toggles the current selection between bold and non-bold - // hotkeys + i -> Toggles the current selection between italic and non-italic - // hotkeys + v -> Toggles the current selection between strike and non-strike - // hotkeys + f -> Removes the formatting tags from the current selection - // hotkeys + y -> Removes the formatting from while textarea - // hotkeys + z -> Inserts a Horizontal Rule - // hotkeys + l -> Toggles the text selection between an unordered list and a normal block - // hotkeys + k -> Toggles the text selection between an ordered list and a normal block - // hotkeys + o -> Draws a line through the middle of the current selection - // hotkeys + w -> Removes any hyperlink from the current selection - var richtTextControl = false - if (_this.browserMagicKey == 'cmd') { - if (!e.altKey && !e.ctrlKey && e.metaKey) { - richtTextControl = true - } - } - else { - if (!e.altKey && e.ctrlKey && !e.metaKey) { - richtTextControl = true - } - } - if (richtTextControl && _this.options.richTextFormatKey[ e.keyCode ]) { - e.preventDefault() - if (e.keyCode == 66) { - document.execCommand('bold') - return true - } - if (e.keyCode == 73) { - document.execCommand('italic') - return true - } - if (e.keyCode == 85) { - document.execCommand('underline') - return true - } - if (e.keyCode == 83) { - document.execCommand('strikeThrough') - return true - } - } - - var hotkeys = false - if (_this.browserHotkeys == 'ctrl+shift') { - if (!e.altKey && e.ctrlKey && !e.metaKey && e.shiftKey) { - hotkeys = true - } - } - else { - if (e.altKey && e.ctrlKey && !e.metaKey) { - hotkeys = true - } - } - - if (hotkeys && (_this.options.richTextFormatKey[ e.keyCode ] - || e.keyCode == 49 - || e.keyCode == 50 - || e.keyCode == 51 - || e.keyCode == 66 - || e.keyCode == 70 - || e.keyCode == 90 - || e.keyCode == 70 - || e.keyCode == 73 - || e.keyCode == 75 - || e.keyCode == 76 - || e.keyCode == 85 - || e.keyCode == 86 - || e.keyCode == 88 - || e.keyCode == 90 - || e.keyCode == 89)) { - e.preventDefault() - - // disable rich text b/u/i - if ( _this.options.mode === 'textonly' ) { - return - } - - if (e.keyCode == 49) { - _this.toggleBlock('h1') - } - if (e.keyCode == 50) { - _this.toggleBlock('h2') - } - if (e.keyCode == 51) { - _this.toggleBlock('h3') - } - if (e.keyCode == 66) { - document.execCommand('bold') - } - if (e.keyCode == 70) { - document.execCommand('removeFormat') - } - if (e.keyCode == 73) { - document.execCommand('italic') - } - if (e.keyCode == 75) { - document.execCommand('insertOrderedList') - } - if (e.keyCode == 76) { - document.execCommand('insertUnorderedList') - } - if (e.keyCode == 85) { - document.execCommand('underline') - } - if (e.keyCode == 86) { - document.execCommand('strikeThrough') - } - if (e.keyCode == 88) { - document.execCommand('unlink') - } - if (e.keyCode == 89) { - var cleanHtml = App.Utils.htmlRemoveRichtext(_this.$element.html()) - _this.$element.html(cleanHtml) - } - if (e.keyCode == 90) { - document.execCommand('insertHorizontalRule') - } - _this.log('content editable richtext key', e.keyCode) + } + if (richtTextControl && this.options.richTextFormatKey[ e.keyCode ]) { + e.preventDefault() + if (e.keyCode == 66) { + document.execCommand('bold') return true } - - // limit check - if ( !_this.allowKey(e) ) { - if ( !_this.maxLengthOk(1) ) { - e.preventDefault() - return - } + if (e.keyCode == 73) { + document.execCommand('italic') + return true } - }) + if (e.keyCode == 85) { + document.execCommand('underline') + return true + } + if (e.keyCode == 83) { + document.execCommand('strikeThrough') + return true + } + } - // just paste text - this.$element.on('paste', function (e) { + var hotkeys = false + if (this.browserHotkeys == 'ctrl+shift') { + if (!e.altKey && e.ctrlKey && !e.metaKey && e.shiftKey) { + hotkeys = true + } + } + else { + if (e.altKey && e.ctrlKey && !e.metaKey) { + hotkeys = true + } + } + + if (hotkeys && (this.options.richTextFormatKey[ e.keyCode ] + || e.keyCode == 49 + || e.keyCode == 50 + || e.keyCode == 51 + || e.keyCode == 66 + || e.keyCode == 70 + || e.keyCode == 90 + || e.keyCode == 70 + || e.keyCode == 73 + || e.keyCode == 75 + || e.keyCode == 76 + || e.keyCode == 85 + || e.keyCode == 86 + || e.keyCode == 88 + || e.keyCode == 90 + || e.keyCode == 89)) { e.preventDefault() - _this.log('paste') - // insert and in case, resize images - var clipboardData - if (e.clipboardData) { // ie - clipboardData = e.clipboardData - } - else if (window.clipboardData) { // ie - clipboardData = window.clipboardData - } - else if (e.originalEvent.clipboardData) { // other browsers - clipboardData = e.originalEvent.clipboardData - } - else { - throw "No clipboardData support" - } - - if (clipboardData && clipboardData.items && clipboardData.items[0]) { - var imageInserted = false - var item = clipboardData.items[0] - if (item.kind == 'file' && (item.type == 'image/png' || item.type == 'image/jpeg')) { - _this.log('paste image', item) - console.log(item) - - var imageFile = item.getAsFile() - var reader = new FileReader() - - reader.onload = function (e) { - var result = e.target.result - var img = document.createElement('img') - img.src = result - maxWidth = _this.$element.width() || 500 - scaleFactor = 2 - //scaleFactor = 1 - //if (window.isRetina && window.isRetina()) { - // scaleFactor = 2 - //} - - insert = function(dataUrl, width, height, isResized) { - //console.log('dataUrl', dataUrl) - //console.log('scaleFactor', scaleFactor, isResized, maxWidth, width, height) - _this.log('image inserted') - result = dataUrl - if (_this.options.imageWidth == 'absolute') { - img = "" - } - else { - img = "" - } - _this.paste(img) - } - - // resize if to big - App.ImageService.resize(img.src, maxWidth, 'auto', scaleFactor, 'image/jpeg', 'auto', insert) - } - reader.readAsDataURL(imageFile) - imageInserted = true - } - } - if (imageInserted) { + // disable rich text b/u/i + if ( this.options.mode === 'textonly' ) { return } - // check existing + paste text for limit - var text, docType - try { - text = clipboardData.getData('text/html') - docType = 'html' - if (!text || text.length === 0) { - docType = 'text' - text = clipboardData.getData('text/plain') - } - if (!text || text.length === 0) { - docType = 'text2' - text = clipboardData.getData('text') - } + if (e.keyCode == 49) { + this.toggleBlock('h1') } - catch (e) { - console.log('Sorry, can\'t insert markup because browser is not supporting it.') - docType = 'text3' - text = clipboardData.getData('text') + if (e.keyCode == 50) { + this.toggleBlock('h2') } - _this.log('paste', docType, text) - - if (docType == 'html') { - if (_this.options.mode === 'textonly') { - if (!_this.options.multiline) { - text = App.Utils.htmlRemoveTags(text) - _this.log('htmlRemoveTags', text) - } - else { - _this.log('htmlRemoveRichtext', text) - text = App.Utils.htmlRemoveRichtext(text) - } - } - else { - _this.log('htmlCleanup', text) - text = App.Utils.htmlCleanup(text) - } - text = text.html() - _this.log('text.html()', text) - - // as fallback, take text - if (!text) { - text = App.Utils.text2html(text.text()) - _this.log('text2html', text) - } + if (e.keyCode == 51) { + this.toggleBlock('h3') } - else { - text = App.Utils.text2html(text) - _this.log('text2html', text) + if (e.keyCode == 66) { + document.execCommand('bold') } - - if (!_this.maxLengthOk(text.length)) { - return + if (e.keyCode == 70) { + document.execCommand('removeFormat') } - - // cleanup - text = App.Utils.removeEmptyLines(text) - _this.log('insert', text) - - _this.paste(text) + if (e.keyCode == 73) { + document.execCommand('italic') + } + if (e.keyCode == 75) { + document.execCommand('insertOrderedList') + } + if (e.keyCode == 76) { + document.execCommand('insertUnorderedList') + } + if (e.keyCode == 85) { + document.execCommand('underline') + } + if (e.keyCode == 86) { + document.execCommand('strikeThrough') + } + if (e.keyCode == 88) { + document.execCommand('unlink') + } + if (e.keyCode == 89) { + var cleanHtml = App.Utils.htmlRemoveRichtext(this.$element.html()) + this.$element.html(cleanHtml) + } + if (e.keyCode == 90) { + document.execCommand('insertHorizontalRule') + } + this.log('content editable richtext key', e.keyCode) return true - }) + } - this.$element.on('dragover', function (e) { - e.stopPropagation() - e.preventDefault() - _this.log('dragover') - }) - - this.$element.on('drop', function (e) { - e.stopPropagation(); - e.preventDefault(); - _this.log('drop') - - var dataTransfer - if (window.dataTransfer) { // ie - dataTransfer = window.dataTransfer - } - else if (e.originalEvent.dataTransfer) { // other browsers - dataTransfer = e.originalEvent.dataTransfer - } - else { - throw "No clipboardData support" + // limit check + if ( !this.allowKey(e) ) { + if ( !this.maxLengthOk(1) ) { + e.preventDefault() + return } + } + } - // x and y coordinates of dropped item - x = e.clientX - y = e.clientY - var file = dataTransfer.files[0] + Plugin.prototype.onPaste = function (e) { + e.preventDefault() + this.log('paste') - // look for images - if (file.type.match('image.*')) { + // insert and in case, resize images + var clipboardData + if (e.clipboardData) { // ie + clipboardData = e.clipboardData + } + else if (window.clipboardData) { // ie + clipboardData = window.clipboardData + } + else if (e.originalEvent.clipboardData) { // other browsers + clipboardData = e.originalEvent.clipboardData + } + else { + throw "No clipboardData support" + } + + if (clipboardData && clipboardData.items && clipboardData.items[0]) { + var imageInserted = false + var item = clipboardData.items[0] + if (item.kind == 'file' && (item.type == 'image/png' || item.type == 'image/jpeg')) { + this.log('paste image', item) + console.log(item) + + var imageFile = item.getAsFile() var reader = new FileReader() - reader.onload = (function(e) { + + reader.onload = $.proxy(function (e) { var result = e.target.result var img = document.createElement('img') img.src = result - maxWidth = _this.$element.width() || 500 + maxWidth = this.$element.width() || 500 scaleFactor = 2 //scaleFactor = 1 //if (window.isRetina && window.isRetina()) { // scaleFactor = 2 //} - //Insert the image at the carat - insert = function(dataUrl, width, height, isResized) { - + insert = $.proxy(function(dataUrl, width, height, isResized) { //console.log('dataUrl', dataUrl) //console.log('scaleFactor', scaleFactor, isResized, maxWidth, width, height) - _this.log('image inserted') + this.log('image inserted') result = dataUrl - if (_this.options.imageWidth == 'absolute') { - img = $("") + if (this.options.imageWidth == 'absolute') { + img = "" } else { - img = $("") + img = "" } - img = img.get(0) - - if (document.caretPositionFromPoint) { - var pos = document.caretPositionFromPoint(x, y) - range = document.createRange(); - range.setStart(pos.offsetNode, pos.offset) - range.collapse() - range.insertNode(img) - } - else if (document.caretRangeFromPoint) { - range = document.caretRangeFromPoint(x, y) - range.insertNode(img) - } - else { - console.log('could not find carat') - } - } + this.paste(img) + }, this) // resize if to big App.ImageService.resize(img.src, maxWidth, 'auto', scaleFactor, 'image/jpeg', 'auto', insert) - }) - reader.readAsDataURL(file) - } - }) + }, this) + reader.readAsDataURL(imageFile) + imageInserted = true + } + } + if (imageInserted) { + return + } + + // check existing + paste text for limit + var text, docType + try { + text = clipboardData.getData('text/html') + docType = 'html' + if (!text || text.length === 0) { + docType = 'text' + text = clipboardData.getData('text/plain') + } + if (!text || text.length === 0) { + docType = 'text2' + text = clipboardData.getData('text') + } + } + catch (e) { + console.log('Sorry, can\'t insert markup because browser is not supporting it.') + docType = 'text3' + text = clipboardData.getData('text') + } + this.log('paste', docType, text) + + if (docType == 'html') { + if (this.options.mode === 'textonly') { + if (!this.options.multiline) { + text = App.Utils.htmlRemoveTags(text) + this.log('htmlRemoveTags', text) + } + else { + this.log('htmlRemoveRichtext', text) + text = App.Utils.htmlRemoveRichtext(text) + } + } + else { + this.log('htmlCleanup', text) + text = App.Utils.htmlCleanup(text) + } + text = text.html() + this.log('text.html()', text) + + // as fallback, take text + if (!text) { + text = App.Utils.text2html(text.text()) + this.log('text2html', text) + } + } + else { + text = App.Utils.text2html(text) + this.log('text2html', text) + } + + if (!this.maxLengthOk(text.length)) { + return + } + + // cleanup + text = App.Utils.removeEmptyLines(text) + this.log('insert', text) + + this.paste(text) + return true + } + + Plugin.prototype.onDragover = function (e) { + e.stopPropagation() + e.preventDefault() + this.log('dragover') + } + + Plugin.prototype.onDrop = function (e) { + e.stopPropagation(); + e.preventDefault(); + this.log('drop') + + var dataTransfer + if (window.dataTransfer) { // ie + dataTransfer = window.dataTransfer + } + else if (e.originalEvent.dataTransfer) { // other browsers + dataTransfer = e.originalEvent.dataTransfer + } + else { + throw "No clipboardData support" + } + + // x and y coordinates of dropped item + x = e.clientX + y = e.clientY + var file = dataTransfer.files[0] + + // look for images + if (file.type.match('image.*')) { + var reader = new FileReader() + reader.onload = (function(e) { + var result = e.target.result + var img = document.createElement('img') + img.src = result + maxWidth = this.$element.width() || 500 + scaleFactor = 2 + //scaleFactor = 1 + //if (window.isRetina && window.isRetina()) { + // scaleFactor = 2 + //} + + //Insert the image at the carat + insert = function(dataUrl, width, height, isResized) { + + //console.log('dataUrl', dataUrl) + //console.log('scaleFactor', scaleFactor, isResized, maxWidth, width, height) + this.log('image inserted') + result = dataUrl + if (this.options.imageWidth == 'absolute') { + img = $("") + } + else { + img = $("") + } + img = img.get(0) + + if (document.caretPositionFromPoint) { + var pos = document.caretPositionFromPoint(x, y) + range = document.createRange(); + range.setStart(pos.offsetNode, pos.offset) + range.collapse() + range.insertNode(img) + } + else if (document.caretRangeFromPoint) { + range = document.caretRangeFromPoint(x, y) + range.insertNode(img) + } + else { + console.log('could not find carat') + } + } + + // resize if to big + App.ImageService.resize(img.src, maxWidth, 'auto', scaleFactor, 'image/jpeg', 'auto', insert) + }) + reader.readAsDataURL(file) + } } // check if key is allowed, even if length limit is reached diff --git a/app/assets/javascripts/app/lib/base/jquery.enableObjectResizingShim.js b/app/assets/javascripts/app/lib/base/jquery.enableObjectResizingShim.js new file mode 100644 index 000000000..7f6ad9fd9 --- /dev/null +++ b/app/assets/javascripts/app/lib/base/jquery.enableObjectResizingShim.js @@ -0,0 +1,155 @@ +(function ($, window, undefined) { + +/* +# mode: textonly/richtext / disable b/i/u/enter + strip on paste +# pasteOnlyText: true +# maxlength: 123 +# multiline: true / disable enter + strip on paste +# placeholder: 'some placeholder' +# +*/ + + var pluginName = 'enableObjectResizingShim', + defaults = { + debug: false + } + + function Plugin(element, options) { + this.element = element + this.$element = $(element) + + this.options = $.extend({}, defaults, options) + + this._defaults = defaults + this._name = pluginName + + // only run if needed + if (!document.queryCommandSupported('enableObjectResizing')) { + this.bindEvents() + } + } + + Plugin.prototype.bindEvents = function () { + this.$element.on('click', 'img', this.createEditor.bind(this)) + this.$element.on('click', this.destroyEditors.bind(this)) + } + + Plugin.prototype.createEditor = function (event) { + event.stopPropagation() + this.destroyEditors() + var $img = $(event.currentTarget) + + if (!$img.hasClass('objectResizingEditorActive')) { + new Editor($img) + } + } + + Plugin.prototype.destroyEditors = function () { + this.$element.find('img.objectResizingEditorActive').each(function(i, img){ + editor = $(img).data('objectResizingEditor') + if(editor){ + editor.destroy() + } + }) + } + + + + /* + + Resize Editor + + */ + + function Editor($element) { + this.$element = $element + this.isResizing = false + + this.$element.data('objectResizingEditor', this) + this.$element.addClass('objectResizingEditorActive') + this.$element.wrap('') + + for (var i=0; i<4; i++) { + this.$element.before('') + } + + $(document).one('click.objectResizingEditor', this.destroy.bind(this)) + $(document).one('keydown.objectResizingEditor', this.onKeydown.bind(this)) + this.$element.on('click.objectResizingEditor', this.stopPropagation.bind(this)) + this.$element.parent().find('.enableObjectResizingShim-handle').on('mousedown', this.startResize.bind(this)) + } + + Editor.prototype.onKeydown = function (event) { + this.destroy() + + switch (event.keyCode) { + case 8: // backspace + this.$element.remove() + break + default: + event.stopPropagation() + break + } + } + + Editor.prototype.stopPropagation = function (event) { + event.stopPropagation() + } + + Editor.prototype.destroy = function (event) { + if(this.isResizing) return + this.$element.off('click.objectResizingEditor') + $(document).off('click.objectResizingEditor') + $(document).off('keydown.objectResizingEditor') + this.$element.removeClass('objectResizingEditorActive') + this.$element.siblings().remove() + this.$element.unwrap() + } + + Editor.prototype.startResize = function (event) { + var $handle = $(event.currentTarget) + this.resizeCorner = $handle.index() + this.resizeDir = this.resizeCorner == 0 || this.resizeCorner == 3 ? -1 : 1 + this.startX = event.pageX + this.startWidth = this.$element.width() + this.$clone = this.$element.clone().css({width: '', height: ''}).addClass('enableObjectResizingShim-clone enableObjectResizingShim-clone--'+ this.resizeCorner) + this.$element.after(this.$clone) + this.isResizing = true + $(document).on('mousemove.enableObjectResizing', this.resize.bind(this)) + $(document).on('mouseup.enableObjectResizing', this.resizeEnd.bind(this)) + } + + Editor.prototype.resize = function (event) { + event.preventDefault() + var dx = event.pageX - this.startX + this.$clone.css('width', this.startWidth + (this.resizeDir * dx)) + } + + Editor.prototype.resizeEnd = function (event) { + $(document).off('mousemove.enableObjectResizing') + $(document).off('mouseup.enableObjectResizing') + + this.$element.css({ + width: this.$clone.width(), + height: this.$clone.height() + }) + this.$clone.remove() + + // reset isResizing with a delay to prevent a mouseup in the editor to trigger a editor-destroy + setTimeout(function(){ + this.isResizing = false + }.bind(this)) + } + + + + + $.fn[pluginName] = function (options) { + return this.each(function () { + if (!$.data(this, 'plugin_' + pluginName)) { + $.data(this, 'plugin_' + pluginName, new Plugin(this, options)) + } + }); + } + +}(jQuery, window)); \ No newline at end of file diff --git a/app/assets/javascripts/app/lib/base/jquery.textmodule.js b/app/assets/javascripts/app/lib/base/jquery.textmodule.js index 40b95fffe..377461abf 100644 --- a/app/assets/javascripts/app/lib/base/jquery.textmodule.js +++ b/app/assets/javascripts/app/lib/base/jquery.textmodule.js @@ -34,160 +34,163 @@ this.ce = $.data(element, 'plugin_ce') } - this.init(); + this.init() } Plugin.prototype.init = function () { this.renderBase() - var _this = this + this.bindEvents() + } - this.$element.on('keydown', function (e) { + Plugin.prototype.bindEvents = function () { + this.$element.on('keydown', this.onKeydown.bind(this)) + this.$element.on('keypress', this.onKeypress.bind(this)) + this.$element.on('focus', this.onFocus.bind(this)) + } - // navigate through item - if (_this.isActive()) { + Plugin.prototype.onFocus = function (e) { + this.close() + } - // esc - if (e.keyCode === 27) { - e.preventDefault() - e.stopPropagation() - _this.close() - return - } - - // enter - if (e.keyCode === 13) { - e.preventDefault() - e.stopPropagation() - var id = _this.$widget.find('.dropdown-menu li.is-active').data('id') - - // as fallback use hovered element - if (!id) { - id = _this.$widget.find('.dropdown-menu li:hover').data('id') - } - - // as fallback first element - if (!id) { - id = _this.$widget.find('.dropdown-menu li:first-child').data('id') - } - _this.take(id) - return - } - - // arrow keys left/right - if (e.keyCode === 37 || e.keyCode === 39) { - e.preventDefault() - e.stopPropagation() - return - } - - // up or down - if (e.keyCode === 38 || e.keyCode === 40) { - e.preventDefault() - e.stopPropagation() - var active = _this.$widget.find('.dropdown-menu li.is-active') - active.removeClass('is-active') - - if (e.keyCode == 38 && active.prev().size()) { - active = active.prev() - } - else if (e.keyCode == 40 && active.next().size()) { - active = active.next() - } - - active.addClass('is-active') - - var menu = _this.$widget.find('.dropdown-menu') - - if (!active.get(0)) { - return - } - if (active.position().top < 0) { - // scroll up - menu.scrollTop( menu.scrollTop() + active.position().top ) - } - else if ( active.position().top + active.height() > menu.height() ) { - // scroll down - var invisibleHeight = active.position().top + active.height() - menu.height() - menu.scrollTop( menu.scrollTop() + invisibleHeight ) - } - } - } + Plugin.prototype.onKeydown = function (e) { + console.log("onKeydown", this.isActive()) + // navigate through item + if (this.isActive()) { // esc if (e.keyCode === 27) { - _this.close() + e.preventDefault() + e.stopPropagation() + this.close() + return } - }) - - // reduce buffer, in case close it - this.$element.on('keydown', function (e) { - - // backspace - if (e.keyCode === 8 && _this.buffer) { - - // backspace + buffer === :: -> close textmodule - if (_this.buffer === '::') { - _this.close(true) - e.preventDefault() - return - } - - // reduce buffer and show new result - var length = _this.buffer.length - _this.buffer = _this.buffer.substr(0, length-1) - _this.log('BS backspace', _this.buffer) - _this.result(_this.buffer.substr(2, length-1)) - } - }) - - // build buffer - this.$element.on('keypress', function (e) { - _this.log('BUFF', _this.buffer, e.keyCode, String.fromCharCode(e.which)) - - // shift - if (e.keyCode === 16) return // enter - if (e.keyCode === 13) return + if (e.keyCode === 13) { + e.preventDefault() + e.stopPropagation() + var id = this.$widget.find('.dropdown-menu li.is-active').data('id') - // arrow keys - if (e.keyCode === 37 || e.keyCode === 38 || e.keyCode === 39 || e.keyCode === 40) return - - // observer other second key - if (_this.buffer === ':' && String.fromCharCode(e.which) !== ':') { - _this.buffer = '' - } - - // oberserve second : - if (_this.buffer === ':' && String.fromCharCode(e.which) === ':') { - _this.buffer = _this.buffer + ':' - } - - // oberserve first : - if (!_this.buffer && String.fromCharCode(e.which) === ':') { - _this.buffer = _this.buffer + ':' - } - - if (_this.buffer && _this.buffer.substr(0,2) === '::') { - - var sign = String.fromCharCode(e.which) - if ( sign && sign !== ':' && e.which != 8 ) { // 8 == backspace - _this.buffer = _this.buffer + sign - //_this.log('BUFF ADD', sign, this.buffer, sign.length, e.which) - } - _this.log('BUFF HINT', _this.buffer, _this.buffer.length, e.which, String.fromCharCode(e.which)) - - if (!_this.isActive()) { - _this.open() + // as fallback use hovered element + if (!id) { + id = this.$widget.find('.dropdown-menu li:hover').data('id') } - _this.result(_this.buffer.substr(2, _this.buffer.length)) + // as fallback first element + if (!id) { + id = this.$widget.find('.dropdown-menu li:first-child').data('id') + } + this.take(id) + return } - }).on('focus', function (e) { - _this.close() - }) - }; + // arrow keys left/right + if (e.keyCode === 37 || e.keyCode === 39) { + e.preventDefault() + e.stopPropagation() + return + } + + // up or down + if (e.keyCode === 38 || e.keyCode === 40) { + e.preventDefault() + e.stopPropagation() + var active = this.$widget.find('.dropdown-menu li.is-active') + active.removeClass('is-active') + + if (e.keyCode == 38 && active.prev().size()) { + active = active.prev() + } + else if (e.keyCode == 40 && active.next().size()) { + active = active.next() + } + + active.addClass('is-active') + + var menu = this.$widget.find('.dropdown-menu') + + if (!active.get(0)) { + return + } + if (active.position().top < 0) { + // scroll up + menu.scrollTop( menu.scrollTop() + active.position().top ) + } + else if ( active.position().top + active.height() > menu.height() ) { + // scroll down + var invisibleHeight = active.position().top + active.height() - menu.height() + menu.scrollTop( menu.scrollTop() + invisibleHeight ) + } + } + } + + // esc + if (e.keyCode === 27) { + this.close() + } + + // reduce buffer, in case close it + // backspace + if (e.keyCode === 8 && this.buffer) { + + // backspace + buffer === :: -> close textmodule + if (this.buffer === '::') { + this.close(true) + e.preventDefault() + return + } + + // reduce buffer and show new result + var length = this.buffer.length + this.buffer = this.buffer.substr(0, length-1) + this.log('BS backspace', this.buffer) + this.result(this.buffer.substr(2, length-1)) + } + } + + Plugin.prototype.onKeypress = function (e) { + this.log('BUFF', this.buffer, e.keyCode, String.fromCharCode(e.which)) + + // shift + if (e.keyCode === 16) return + + // enter + if (e.keyCode === 13) return + + // arrow keys + if (e.keyCode === 37 || e.keyCode === 38 || e.keyCode === 39 || e.keyCode === 40) return + + // observer other second key + if (this.buffer === ':' && String.fromCharCode(e.which) !== ':') { + this.buffer = '' + } + + // oberserve second : + if (this.buffer === ':' && String.fromCharCode(e.which) === ':') { + this.buffer = this.buffer + ':' + } + + // oberserve first : + if (!this.buffer && String.fromCharCode(e.which) === ':') { + this.buffer = this.buffer + ':' + } + + if (this.buffer && this.buffer.substr(0,2) === '::') { + + var sign = String.fromCharCode(e.which) + if ( sign && sign !== ':' && e.which != 8 ) { // 8 == backspace + this.buffer = this.buffer + sign + //this.log('BUFF ADD', sign, this.buffer, sign.length, e.which) + } + this.log('BUFF HINT', this.buffer, this.buffer.length, e.which, String.fromCharCode(e.which)) + + if (!this.isActive()) { + this.open() + } + + this.result(this.buffer.substr(2, this.buffer.length)) + } + } // create base template Plugin.prototype.renderBase = function() { diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 020860823..809f2b57b 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -10,6 +10,7 @@ *= require ./font.css *= require ./svg-dimensions.css *= require ./highlighter-github.css + *= require ./jquery.enableObjectResizingShim.css *= require ./zammad.scss * *= require_tree ./custom/ diff --git a/app/assets/stylesheets/jquery.enableObjectResizingShim.css b/app/assets/stylesheets/jquery.enableObjectResizingShim.css new file mode 100644 index 000000000..bce47d8b4 --- /dev/null +++ b/app/assets/stylesheets/jquery.enableObjectResizingShim.css @@ -0,0 +1,77 @@ +.enableObjectResizingShim { + box-shadow: 0 0 0 1px black; + position: relative; + display: inline-block !important; + vertical-align: top; +} + +.enableObjectResizingShim-handle { + position: absolute; + width: 20px; + height: 20px; + margin: -10px; + left: 0; + top: 0; + cursor: nwse-resize; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.enableObjectResizingShim-handle:after { + content: ""; + position: absolute; + left: 6px; + top: 6px; + width: 8px; + height: 8px; + background: white; + border: 1px solid black; +} + +.enableObjectResizingShim-handle:hover:after { + background: black; +} + +.enableObjectResizingShim-handle:nth-child(2) { + left: 100%; + cursor: nesw-resize; +} + +.enableObjectResizingShim-handle:nth-child(3) { + left: 100%; + top: 100%; +} + +.enableObjectResizingShim-handle:nth-child(4) { + top: 100%; + cursor: nesw-resize; +} + +.enableObjectResizingShim-clone { + position: absolute; + width: 100%; + height: auto; + opacity: 0.5; + border: 1px dashed black; +} + +.enableObjectResizingShim-clone--0 { + right: 0; + bottom: 0; +} + +.enableObjectResizingShim-clone--1 { + left: 0; + bottom: 0; +} + +.enableObjectResizingShim-clone--2 { + left: 0; + top: 0; +} + +.enableObjectResizingShim-clone--3 { + top: 0; + right: 0; +} \ No newline at end of file