Moved to jquery 1.11.2, moved to medium.js improved browser tests.

This commit is contained in:
Martin Edenhofer 2014-12-08 10:39:05 +01:00
parent 0fcfca12e9
commit c5bb0f4393
16 changed files with 19927 additions and 916 deletions

View file

@ -1,367 +0,0 @@
//@ sourceMappingURL=jquery.caret.map
/*
Implement Github like autocomplete mentions
http://ichord.github.com/At.js
Copyright (c) 2013 chord.luo@gmail.com
Licensed under the MIT license.
*/
/*
本插件操作 textarea 或者 input 内的插入符
只实现了获得插入符在文本框中的位置我设置
插入符的位置.
*/
(function() {
(function(factory) {
if (typeof define === 'function' && define.amd) {
return define(['jquery'], factory);
} else {
return factory(window.jQuery);
}
})(function($) {
"use strict";
var EditableCaret, InputCaret, Mirror, Utils, discoveryIframeOf, methods, oDocument, oFrame, oWindow, pluginName, setContextBy;
pluginName = 'caret';
EditableCaret = (function() {
function EditableCaret($inputor) {
this.$inputor = $inputor;
this.domInputor = this.$inputor[0];
}
EditableCaret.prototype.setPos = function(pos) {
return this.domInputor;
};
EditableCaret.prototype.getIEPosition = function() {
return this.getPosition();
};
EditableCaret.prototype.getPosition = function() {
var inputor_offset, offset;
offset = this.getOffset();
inputor_offset = this.$inputor.offset();
offset.left -= inputor_offset.left;
offset.top -= inputor_offset.top;
return offset;
};
EditableCaret.prototype.getOldIEPos = function() {
var preCaretTextRange, textRange;
textRange = oDocument.selection.createRange();
preCaretTextRange = oDocument.body.createTextRange();
preCaretTextRange.moveToElementText(this.domInputor);
preCaretTextRange.setEndPoint("EndToEnd", textRange);
return preCaretTextRange.text.length;
};
EditableCaret.prototype.getPos = function() {
var clonedRange, pos, range;
if (range = this.range()) {
clonedRange = range.cloneRange();
clonedRange.selectNodeContents(this.domInputor);
clonedRange.setEnd(range.endContainer, range.endOffset);
pos = clonedRange.toString().length;
clonedRange.detach();
return pos;
} else if (oDocument.selection) {
return this.getOldIEPos();
}
};
EditableCaret.prototype.getOldIEOffset = function() {
var range, rect;
range = oDocument.selection.createRange().duplicate();
range.moveStart("character", -1);
rect = range.getBoundingClientRect();
return {
height: rect.bottom - rect.top,
left: rect.left,
top: rect.top
};
};
EditableCaret.prototype.getOffset = function(pos) {
var clonedRange, offset, range, rect;
if (oWindow.getSelection && (range = this.range())) {
if (range.endOffset - 1 < 0) {
return null;
}
clonedRange = range.cloneRange();
clonedRange.setStart(range.endContainer, range.endOffset - 1);
clonedRange.setEnd(range.endContainer, range.endOffset);
rect = clonedRange.getBoundingClientRect();
offset = {
height: rect.height,
left: rect.left + rect.width,
top: rect.top
};
clonedRange.detach();
} else if (oDocument.selection) {
offset = this.getOldIEOffset();
}
if (offset) {
offset.top += $(oWindow).scrollTop();
offset.left += $(oWindow).scrollLeft();
}
return offset;
};
EditableCaret.prototype.range = function() {
var sel;
if (!oWindow.getSelection) {
return;
}
sel = oWindow.getSelection();
if (sel.rangeCount > 0) {
return sel.getRangeAt(0);
} else {
return null;
}
};
return EditableCaret;
})();
InputCaret = (function() {
function InputCaret($inputor) {
this.$inputor = $inputor;
this.domInputor = this.$inputor[0];
}
InputCaret.prototype.getIEPos = function() {
var endRange, inputor, len, normalizedValue, pos, range, textInputRange;
inputor = this.domInputor;
range = oDocument.selection.createRange();
pos = 0;
if (range && range.parentElement() === inputor) {
normalizedValue = inputor.value.replace(/\r\n/g, "\n");
len = normalizedValue.length;
textInputRange = inputor.createTextRange();
textInputRange.moveToBookmark(range.getBookmark());
endRange = inputor.createTextRange();
endRange.collapse(false);
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
pos = len;
} else {
pos = -textInputRange.moveStart("character", -len);
}
}
return pos;
};
InputCaret.prototype.getPos = function() {
if (oDocument.selection) {
return this.getIEPos();
} else {
return this.domInputor.selectionStart;
}
};
InputCaret.prototype.setPos = function(pos) {
var inputor, range;
inputor = this.domInputor;
if (oDocument.selection) {
range = inputor.createTextRange();
range.move("character", pos);
range.select();
} else if (inputor.setSelectionRange) {
inputor.setSelectionRange(pos, pos);
}
return inputor;
};
InputCaret.prototype.getIEOffset = function(pos) {
var h, textRange, x, y;
textRange = this.domInputor.createTextRange();
pos || (pos = this.getPos());
textRange.move('character', pos);
x = textRange.boundingLeft;
y = textRange.boundingTop;
h = textRange.boundingHeight;
return {
left: x,
top: y,
height: h
};
};
InputCaret.prototype.getOffset = function(pos) {
var $inputor, offset, position;
$inputor = this.$inputor;
if (oDocument.selection) {
offset = this.getIEOffset(pos);
offset.top += $(oWindow).scrollTop() + $inputor.scrollTop();
offset.left += $(oWindow).scrollLeft() + $inputor.scrollLeft();
return offset;
} else {
offset = $inputor.offset();
position = this.getPosition(pos);
return offset = {
left: offset.left + position.left - $inputor.scrollLeft(),
top: offset.top + position.top - $inputor.scrollTop(),
height: position.height
};
}
};
InputCaret.prototype.getPosition = function(pos) {
var $inputor, at_rect, end_range, format, html, mirror, start_range;
$inputor = this.$inputor;
format = function(value) {
return $('<div></div>').text(value).html().replace(/\r\n|\r|\n/g, "<br/>").replace(/\s/g, "&nbsp;");
};
if (pos === void 0) {
pos = this.getPos();
}
start_range = $inputor.val().slice(0, pos);
end_range = $inputor.val().slice(pos);
html = "<span style='position: relative; display: inline;'>" + format(start_range) + "</span>";
html += "<span id='caret' style='position: relative; display: inline;'>|</span>";
html += "<span style='position: relative; display: inline;'>" + format(end_range) + "</span>";
mirror = new Mirror($inputor);
return at_rect = mirror.create(html).rect();
};
InputCaret.prototype.getIEPosition = function(pos) {
var h, inputorOffset, offset, x, y;
offset = this.getIEOffset(pos);
inputorOffset = this.$inputor.offset();
x = offset.left - inputorOffset.left;
y = offset.top - inputorOffset.top;
h = offset.height;
return {
left: x,
top: y,
height: h
};
};
return InputCaret;
})();
Mirror = (function() {
Mirror.prototype.css_attr = ["borderBottomWidth", "borderLeftWidth", "borderRightWidth", "borderTopStyle", "borderRightStyle", "borderBottomStyle", "borderLeftStyle", "borderTopWidth", "boxSizing", "fontFamily", "fontSize", "fontWeight", "height", "letterSpacing", "lineHeight", "marginBottom", "marginLeft", "marginRight", "marginTop", "outlineWidth", "overflow", "overflowX", "overflowY", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "textAlign", "textOverflow", "textTransform", "whiteSpace", "wordBreak", "wordWrap"];
function Mirror($inputor) {
this.$inputor = $inputor;
}
Mirror.prototype.mirrorCss = function() {
var css,
_this = this;
css = {
position: 'absolute',
left: -9999,
top: 0,
zIndex: -20000
};
if (this.$inputor.prop('tagName') === 'TEXTAREA') {
this.css_attr.push('width');
}
$.each(this.css_attr, function(i, p) {
return css[p] = _this.$inputor.css(p);
});
return css;
};
Mirror.prototype.create = function(html) {
this.$mirror = $('<div></div>');
this.$mirror.css(this.mirrorCss());
this.$mirror.html(html);
this.$inputor.after(this.$mirror);
return this;
};
Mirror.prototype.rect = function() {
var $flag, pos, rect;
$flag = this.$mirror.find("#caret");
pos = $flag.position();
rect = {
left: pos.left,
top: pos.top,
height: $flag.height()
};
this.$mirror.remove();
return rect;
};
return Mirror;
})();
Utils = {
contentEditable: function($inputor) {
return !!($inputor[0].contentEditable && $inputor[0].contentEditable === 'true');
}
};
methods = {
pos: function(pos) {
if (pos || pos === 0) {
return this.setPos(pos);
} else {
return this.getPos();
}
},
position: function(pos) {
if (oDocument.selection) {
return this.getIEPosition(pos);
} else {
return this.getPosition(pos);
}
},
offset: function(pos) {
var offset;
offset = this.getOffset(pos);
return offset;
}
};
oDocument = null;
oWindow = null;
oFrame = null;
setContextBy = function(settings) {
var iframe;
if (iframe = settings != null ? settings.iframe : void 0) {
oFrame = iframe;
oWindow = iframe.contentWindow;
return oDocument = iframe.contentDocument || oWindow.document;
} else {
oFrame = void 0;
oWindow = window;
return oDocument = document;
}
};
discoveryIframeOf = function($dom) {
var error;
oDocument = $dom[0].ownerDocument;
oWindow = oDocument.defaultView || oDocument.parentWindow;
try {
return oFrame = oWindow.frameElement;
} catch (_error) {
error = _error;
}
};
$.fn.caret = function(method, value, settings) {
var caret;
if (methods[method]) {
if ($.isPlainObject(value)) {
setContextBy(value);
value = void 0;
} else {
setContextBy(settings);
}
caret = Utils.contentEditable(this) ? new EditableCaret(this) : new InputCaret(this);
return methods[method].apply(caret, [value]);
} else {
return $.error("Method " + method + " does not exist on jQuery.caret");
}
};
$.fn.caret.EditableCaret = EditableCaret;
$.fn.caret.InputCaret = InputCaret;
$.fn.caret.Utils = Utils;
return $.fn.caret.apis = methods;
});
}).call(this);

View file

@ -1,203 +0,0 @@
/**
* jQuery plugin for getting position of cursor in textarea
* @license under GNU license
* @author Bevis Zhao (i@bevis.me, http://bevis.me)
*/
// workaround to work with jquery 1.9
// http://api.jquery.com/jQuery.browser/
(function(){
if (!jQuery.browser) {
jQuery.browser={};
jQuery.browser.msie = false;
jQuery.browser.version = 0;
if( navigator.userAgent.match(/MSIE ([0-9]+)\./) ){
jQuery.browser.msie = true;
jQuery.browser.version = RegExp.$1;
}
else if ( !navigator.userAgent.match(/mozilla/i) && !navigator.userAgent.match(/webkit/i) ){
jQuery.browser.mozilla = true;
}
}
})();
$(function() {
var calculator = {
// key styles
primaryStyles: ['fontFamily', 'fontSize', 'fontWeight', 'fontVariant', 'fontStyle',
'paddingLeft', 'paddingTop', 'paddingBottom', 'paddingRight',
'marginLeft', 'marginTop', 'marginBottom', 'marginRight',
'borderLeftColor', 'borderTopColor', 'borderBottomColor', 'borderRightColor',
'borderLeftStyle', 'borderTopStyle', 'borderBottomStyle', 'borderRightStyle',
'borderLeftWidth', 'borderTopWidth', 'borderBottomWidth', 'borderRightWidth',
'line-height', 'outline'],
specificStyle: {
'word-wrap': 'break-word',
'overflow-x': 'hidden',
'overflow-y': 'auto'
},
simulator : $('<div id="textarea_simulator"/>').css({
position: 'absolute',
top: 0,
left: 0,
visibility: 'hidden'
}).appendTo(document.body),
toHtml : function(text) {
return text.replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\n/g, '<br>')
.split(' ').join('<span style="white-space:prev-wrap">&nbsp;</span>');
},
// calculate position
getCaretPosition: function() {
var cal = calculator, self = this, element = self[0], elementOffset = self.offset();
// IE has easy way to get caret offset position
if ($.browser.msie && $.browser.version <= 9) {
// must get focus first
element.focus();
var range = document.selection.createRange();
$('#hskeywords').val(element.scrollTop);
return {
left: range.boundingLeft - elementOffset.left,
top: parseInt(range.boundingTop) - elementOffset.top + element.scrollTop
+ document.documentElement.scrollTop + parseInt(self.getComputedStyle("fontSize"))
};
}
cal.simulator.empty();
// clone primary styles to imitate textarea
$.each(cal.primaryStyles, function(index, styleName) {
self.cloneStyle(cal.simulator, styleName);
});
// caculate width and height
cal.simulator.css($.extend({
'width': self.width(),
'height': self.height()
}, cal.specificStyle));
var value = (self.val() || self.text()), cursorPosition = self.getCursorPosition();
var beforeText = value.substring(0, cursorPosition),
afterText = value.substring(cursorPosition);
var before = $('<span class="before"/>').html(cal.toHtml(beforeText)),
focus = $('<span class="focus"/>'),
after = $('<span class="after"/>').html(cal.toHtml(afterText));
cal.simulator.append(before).append(focus).append(after);
var focusOffset = focus.offset(), simulatorOffset = cal.simulator.offset();
// alert(focusOffset.left + ',' + simulatorOffset.left + ',' + element.scrollLeft);
return {
top: focusOffset.top - simulatorOffset.top - element.scrollTop
// calculate and add the font height except Firefox
+ ($.browser.mozilla ? 0 : parseInt(self.getComputedStyle("fontSize"))),
left: focus[0].offsetLeft - cal.simulator[0].offsetLeft - element.scrollLeft
};
}
};
$.fn.extend({
setCursorPosition : function(position){
if(this.length == 0) return this;
return $(this).setSelection(position, position);
},
setSelection: function(selectionStart, selectionEnd) {
if(this.length == 0) return this;
input = this[0];
if (input.createTextRange) {
var range = input.createTextRange();
range.collapse(true);
range.moveEnd('character', selectionEnd);
range.moveStart('character', selectionStart);
range.select();
} else if (input.setSelectionRange) {
input.focus();
input.setSelectionRange(selectionStart, selectionEnd);
} else {
var el = this.get(0);
var range = document.createRange();
range.collapse(true);
range.setStart(el.childNodes[0], selectionStart);
range.setEnd(el.childNodes[0], selectionEnd);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
return this;
},
getComputedStyle: function(styleName) {
if (this.length == 0) return;
var thiz = this[0];
var result = this.css(styleName);
result = result || ($.browser.msie ?
thiz.currentStyle[styleName]:
document.defaultView.getComputedStyle(thiz, null)[styleName]);
return result;
},
// easy clone method
cloneStyle: function(target, styleName) {
var styleVal = this.getComputedStyle(styleName);
if (!!styleVal) {
$(target).css(styleName, styleVal);
}
},
cloneAllStyle: function(target, style) {
var thiz = this[0];
for (var styleName in thiz.style) {
var val = thiz.style[styleName];
typeof val == 'string' || typeof val == 'number'
? this.cloneStyle(target, styleName)
: NaN;
}
},
getCursorPosition : function() {
var element = input = this[0];
var value = (input.value || input.innerText)
if(!this.data("lastCursorPosition")){
this.data("lastCursorPosition",0);
}
var lastCursorPosition = this.data("lastCursorPosition");
if (document.selection) {
input.focus();
var sel = document.selection.createRange();
var selLen = document.selection.createRange().text.length;
sel.moveStart('character', -value.length);
lastCursorPosition = sel.text.length - selLen;
} else if (input.selectionStart || input.selectionStart == '0') {
return input.selectionStart;
} else if (typeof window.getSelection != "undefined" && window.getSelection().rangeCount>0) {
try{
var selection = window.getSelection();
var range = selection.getRangeAt(0);
var preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
lastCursorPosition = preCaretRange.toString().length;
}catch(e){
lastCursorPosition = this.data("lastCursorPosition");
}
} else if (typeof document.selection != "undefined" && document.selection.type != "Control") {
var textRange = document.selection.createRange();
var preCaretTextRange = document.body.createTextRange();
preCaretTextRange.moveToElementText(element);
preCaretTextRange.setEndPoint("EndToEnd", textRange);
lastCursorPosition = preCaretTextRange.text.length;
}
this.data("lastCursorPosition",lastCursorPosition);
return lastCursorPosition;
},
getCaretPosition: calculator.getCaretPosition
});
});

View file

@ -6,39 +6,12 @@
# maxlength: 123 # maxlength: 123
# multiline: true / disable enter + strip on paste # multiline: true / disable enter + strip on paste
# placeholder: 'some placeholder' # placeholder: 'some placeholder'
#
*/ */
var pluginName = 'ce', var pluginName = 'ce',
defaults = { defaults = {
mode: 'richtext', mode: 'richtext',
multiline: true, multiline: true,
allowKey: {
8: true, // backspace
9: true, // tab
16: true, // shift
17: true, // ctrl
18: true, // alt
20: true, // cabslock
37: true, // up
38: true, // right
39: true, // down
40: true, // left
91: true, // cmd left
92: true, // cmd right
},
extraAllowKey: {
65: true, // a + ctrl - select all
67: true, // c + ctrl - copy
86: true, // v + ctrl - paste
88: true, // x + ctrl - cut
90: true, // z + ctrl - undo
},
richTextFormatKey: {
66: true, // b
73: true, // i
85: true, // u
},
}; };
function Plugin( element, options ) { function Plugin( element, options ) {
@ -47,232 +20,34 @@
this.options = $.extend( {}, defaults, options) ; this.options = $.extend( {}, defaults, options) ;
this._defaults = defaults;
this._name = pluginName;
// take placeholder from markup // take placeholder from markup
if ( !this.options.placeholder && this.$element.data('placeholder') ) { if ( !this.options.placeholder && this.$element.data('placeholder') ) {
this.options.placeholder = this.$element.data('placeholder') this.options.placeholder = this.$element.data('placeholder')
} }
this._defaults = defaults; // link input
this._name = pluginName; if ( !this.options.multiline ) {
editorMode = Medium.inlineMode
this.preventInput = false
this.init();
} }
Plugin.prototype.init = function () { // link textarea
var _this = this else if ( this.options.multiline && this.options.mode != 'richtext' ) {
editorMode = Medium.partialMode
// set focus class
this.$element.on('focus', function (e) {
_this.$element.closest('.form-control').addClass('focus')
}).on('blur', function (e) {
_this.$element.closest('.form-control').removeClass('focus')
})
// process placeholder
if ( this.options.placeholder ) {
this.updatePlaceholder( 'add' )
this.$element.on('focus', function (e) {
_this.updatePlaceholder( 'remove' )
}).on('blur', function (e) {
_this.updatePlaceholder( 'add' )
})
} }
// maxlength check // rich text
//this.options.maxlength = 10
if ( this.options.maxlength ) {
this.$element.on('keydown', function (e) {
console.log('maxlength', e.keyCode, _this.allowKey(e))
// check control key
if ( _this.allowKey(e) ) {
_this.maxLengthOk()
}
// check type ahead key
else { else {
if ( !_this.maxLengthOk( true ) ) { editorMode = Medium.richMode
e.preventDefault()
}
}
}).on('keyup', function (e) {
// check control key
if ( _this.allowKey(e) ) {
_this.maxLengthOk()
}
// check type ahead key
else {
if ( !_this.maxLengthOk( true ) ) {
e.preventDefault()
}
}
}).on('focus', function (e) {
_this.maxLengthOk()
}).on('blur', function (e) {
_this.maxLengthOk()
})
} }
// handle enter // max length validation
this.$element.on('keydown', function (e) { var validation = function(element) {
console.log('keydown', e.keyCode) console.log('pp', element, $(element))
if ( _this.preventInput ) {
console.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
}
// limit check
if ( !_this.maxLengthOk( true ) ) {
e.preventDefault()
return
}
//newLine = "<br></br>"
newLine = "\n<br>"
if (document.selection) {
var range = document.selection.createRange()
newLine = "<br/>" // ie is not supporting \n :(
range.pasteHTML(newLine)
}
else {
// workaround for chrome - insert <br> directly after <br> is ignored -
// insert <br>, if it hasn't change add it twice again
var oldValue = _this.$element.html().trim()
document.execCommand('insertHTML', false, newLine)
var newValue = _this.$element.html().trim()
console.log('ON', oldValue, '--', newValue)
//if ( oldValue == newValue || oldValue == newValue.substr( 0, newValue.length - newLine.length ) ) {
//if ( oldValue == newValue.substr( 0, newValue.length - newLine.length ) ) {
if ( oldValue == newValue ) {
var oldValue = _this.$element.html().trim()
document.execCommand('insertHTML', false, newLine)
console.log('Autoinsert 1th-br')
var newValue = _this.$element.html().trim()
if ( oldValue == newValue ) {
console.log('Autoinsert 2th-br')
document.execCommand('insertHTML', false, ' ' + newLine) // + newLine)
}
}
}
// prevent the default behaviour of return key pressed
e.preventDefault()
return false
}
})
// just paste text
if ( this.options.mode === 'textonly' ) {
this.$element.on('paste', function (e) {
e.preventDefault()
var text
if (window.clipboardData) { // IE
text = window.clipboardData.getData('Text')
}
else {
text = (e.originalEvent || e).clipboardData.getData('text/plain')
}
var overlimit = false
if (text) {
// replace new lines
if ( !_this.options.multiline ) {
text = text.replace(/\n/g, '')
text = text.replace(/\r/g, '')
text = text.replace(/\t/g, '')
}
// limit length, limit paste string
if ( _this.options.maxlength ) {
var pasteLength = text.length
var currentLength = _this.$element.text().length
var overSize = ( currentLength + pasteLength ) - _this.options.maxlength
if ( overSize > 0 ) {
text = text.substr( 0, pasteLength - overSize )
overlimit = true
}
}
// insert new text
if (document.selection) { // IE
var range = document.selection.createRange()
range.pasteHTML(text)
}
else {
document.execCommand('inserttext', false, text)
}
_this.maxLengthOk( overlimit )
}
})
}
// disable rich text b/u/i
if ( this.options.mode === 'textonly' ) {
this.$element.on('keydown', function (e) {
if ( _this.richTextKey(e) ) {
e.preventDefault()
}
})
}
};
// add/remove placeholder
Plugin.prototype.updatePlaceholder = function(type) {
if ( !this.options.placeholder ) {
return
}
var holder = this.$element
var text = holder.text().trim()
var placeholder = '<span class="placeholder">' + this.options.placeholder + '</span>'
// add placholder if no text exists
if ( type === 'add') {
if ( !text ) {
holder.html( placeholder )
}
}
// empty placeholder text
else {
if ( text === this.options.placeholder ) {
setTimeout(function(){
document.execCommand('selectAll', false, '');
document.execCommand('delete', false, '');
document.execCommand('selectAll', false, '');
document.execCommand('removeFormat', false, '');
}, 100);
}
}
}
// disable/enable input
Plugin.prototype.input = function(type) {
if ( type === 'off' ) {
this.preventInput = true
}
else {
this.preventInput = false
}
}
// max length check
Plugin.prototype.maxLengthOk = function(typeAhead) {
var length = this.$element.text().length
if (typeAhead) {
length = length + 1
}
if ( length > this.options.maxlength ) {
// try to set error on framework form // try to set error on framework form
var parent = this.$element.parent().parent() var parent = $(element).parent().parent()
if ( parent.hasClass('controls') ) { if ( parent.hasClass('controls') ) {
parent.addClass('has-error') parent.addClass('has-error')
setTimeout($.proxy(function(){ setTimeout($.proxy(function(){
@ -284,42 +59,24 @@
// set validation on element // set validation on element
else { else {
this.$element.addClass('invalid') $(element).addClass('invalid')
setTimeout($.proxy(function(){ setTimeout($.proxy(function(){
this.$element.removeClass('invalid') $(element).removeClass('invalid')
}, this), 1000) }, this), 1000)
return false return false
} }
} }
return true new Medium({
} element: element,
modifier: 'auto',
// check if key is allowed, even if length limit is reached placeholder: this.options.placeholder || '',
Plugin.prototype.allowKey = function(e) { autofocus: false,
if ( this.options.allowKey[ e.keyCode ] ) { autoHR: false,
return true mode: editorMode,
} maxLength: this.options.maxlength || -1,
if ( ( e.ctrlKey || e.metaKey ) && this.options.extraAllowKey[ e.keyCode ] ) { maxLengthReached: validation,
return true });
}
return false
}
// check if rich text key is pressed
Plugin.prototype.richTextKey = function(e) {
// e.altKey
// e.ctrlKey
// e.metaKey
// on mac block e.metaKey + i/b/u
if ( !e.altKey && e.metaKey && this.options.richTextFormatKey[ e.keyCode ] ) {
return true
}
// on win block e.ctrlKey + i/b/u
if ( !e.altKey && e.ctrlKey && this.options.richTextFormatKey[ e.keyCode ] ) {
return true
}
return false
} }
// get value // get value
@ -332,9 +89,17 @@
// strip html signes if multi line exists // strip html signes if multi line exists
if ( this.options.multiline ) { if ( this.options.multiline ) {
var text = this.$element.html() var text = this.$element.html()
//console.log('h2a 1', text)
text = text.replace(/<p><br><\/p>/g, "\n") // new line as /p
text = text.replace(/<p><br\/><\/p>/g, "\n") // new line as /p
text = text.replace(/<\/p>/g, "\n") // new line as /p
text = text.replace(/<br>/g, "\n") // new line as br text = text.replace(/<br>/g, "\n") // new line as br
text = text.replace(/<div>/g, "\n") // in some caes, new line als div text = text.replace(/<\/div>/g, "\n") // in some caes, new line als div
text = $("<div>" + text + "</div>").text().trim() text = text.replace(/<.+?>/g, "") // new line as /p
text = $("<div>" + text + "</div>").text()
text = text.replace(/&nbsp;/g, " ")
text = text.replace(/\s+$/g, '');
//console.log('h2a 2', text)
return text return text
} }
return this.$element.text().trim() return this.$element.text().trim()
@ -358,18 +123,3 @@
} }
}(jQuery)); }(jQuery));
/* TODO: paste
@$('.ticket-title-update').bind(
'drop'
(e) =>
e.preventDefault()
t2 = e.originalEvent.dataTransfer.getData("text/plain")# : window.event.dataTransfer.getData("Text");
@log('drop', t2, e.keyCode, e.clipboardData, e, $(e.target).text().length)
document.execCommand('inserttext', false, '123123');
#document.execCommand('inserttext', false, prompt('Paste something.'));
)
*/

View file

@ -174,25 +174,29 @@
// update widget position // update widget position
Plugin.prototype.updatePosition = function() { Plugin.prototype.updatePosition = function() {
console.log('uP')
this.$widget.find('.dropdown-menu').scrollTop( 300 ); this.$widget.find('.dropdown-menu').scrollTop( 300 );
//if ( !this.$element.is(':visible') ) return if ( !this.$element.is(':visible') ) return
var position = this.$element.caret('position');
console.log('PP', position) // get cursor position
var marker = '<span id="js-cursor-position"></span>'
Medium.Injector.prototype.inject( marker )
position = $('#js-cursor-position').position()
$('#js-cursor-position').remove()
if (!position) return if (!position) return
var widgetHeight = this.$widget.find('ul').height() + 85
this.$widget.css('top', position.top - widgetHeight) // set position of widget
if ( !this.isActive() ) { var height = this.$element.height()
var widgetHeight = this.$widget.find('ul').height() //+ 60 // + height
var top = -( widgetHeight + height ) + position.top
this.$widget.css('top', top)
this.$widget.css('left', position.left) this.$widget.css('left', position.left)
} }
}
// open widget // open widget
Plugin.prototype.open = function() { Plugin.prototype.open = function() {
this.active = true this.active = true
if (this.ce) { this.updatePosition()
this.ce.input('off')
}
b = $.proxy(function() { b = $.proxy(function() {
this.$widget.addClass('open') this.$widget.addClass('open')
}, this) }, this)
@ -203,9 +207,6 @@
Plugin.prototype.close = function() { Plugin.prototype.close = function() {
this.active = false this.active = false
this.cutInput() this.cutInput()
if (this.ce) {
this.ce.input('on')
}
this.$widget.removeClass('open') this.$widget.removeClass('open')
} }
@ -214,6 +215,36 @@
return this.active return this.active
} }
// paste some content
Plugin.prototype.paste = function(string) {
if (document.selection) { // IE
var range = document.selection.createRange()
range.pasteHTML(string)
}
else {
document.execCommand('insertHTML', false, string)
}
}
// cut some content
Plugin.prototype.cut = function(string) {
var sel = window.getSelection()
if ( !sel || sel.rangeCount < 1) {
return
}
var range = sel.getRangeAt(0)
var clone = range.cloneRange()
// improve error handling
start = range.startOffset - string.length
if (start < 0) {
start = 0
}
clone.setStart(range.startContainer, start)
clone.setEnd(range.startContainer, range.startOffset)
clone.deleteContents()
}
// select text module and insert into text // select text module and insert into text
Plugin.prototype.take = function(id) { Plugin.prototype.take = function(id) {
if (!id) { if (!id) {
@ -223,15 +254,9 @@
for (var i = 0; i < this.collection.length; i++) { for (var i = 0; i < this.collection.length; i++) {
var item = this.collection[i] var item = this.collection[i]
if ( item.id == id ) { if ( item.id == id ) {
var content = item.content + "\n" var content = item.content + "<br/>\n"
this.cutInput() this.cutInput()
if (document.selection) { // IE this.paste(content)
var range = document.selection.createRange()
range.pasteHTML(content)
}
else {
document.execCommand('insertHTML', false, content)
}
this.close() this.close()
return return
} }
@ -248,17 +273,7 @@
this.buffer = '' this.buffer = ''
return return
} }
var range = sel.getRangeAt(0) this.cut(this.buffer)
var clone = range.cloneRange()
// improve error handling
start = range.startOffset - this.buffer.length
if (start < 0) {
start = 0
}
clone.setStart(range.startContainer, start)
clone.setEnd(range.startContainer, range.startOffset)
clone.deleteContents()
this.buffer = '' this.buffer = ''
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,3 @@
<div class="richtext form-control"> <div class="richtext form-control">
<div contenteditable="true" id="<%= @attribute.id %>" data-name="<%= @attribute.name %>" class="<%= @attribute.class %>"><%= @attribute.value %></div> <div contenteditable="true" id="<%= @attribute.id %>" data-name="<%= @attribute.name %>" class="<%= @attribute.class %>"><%- @A2H(@attribute.value) %></div>
</div> </div>

View file

@ -6,7 +6,7 @@
// //
//= require ./app/lib/core/jquery-2.1.1.js //= require ./app/lib/core/jquery-2.1.1.js
//= require ./app/lib/core/jquery-ui-1.8.23.custom.min.js //= require ./app/lib/core/jquery-ui-1.11.2.js
//= require ./app/lib/core/underscore-1.7.0.js //= require ./app/lib/core/underscore-1.7.0.js
//= require ./app/lib/animations/velocity.min.js //= require ./app/lib/animations/velocity.min.js

View file

@ -6,11 +6,12 @@
*= require ./bootstrap.css *= require ./bootstrap.css
*= require ./cropper.min.css *= require ./cropper.min.css
*= require ./fineuploader.css *= require ./fineuploader.css
*= require ./ui-lightness/jquery-ui-1.8.23.custom.css *= require ./ui-lightness/jquery-ui-1.11.2.css
*= require ./jquery.noty.css *= require ./jquery.noty.css
*= require ./bootstrap-tokenfield.css *= require ./bootstrap-tokenfield.css
*= require ./noty_theme_twitter.css *= require ./noty_theme_twitter.css
*= require ./sew.css *= require ./sew.css
*= require ./medium.css
*= require ./fira-sans.css *= require ./fira-sans.css
*= require ./zammad.css.scss *= require ./zammad.css.scss
* *

View file

@ -0,0 +1,44 @@
.Medium{
position: relative;
min-height: 10px;
}
.Medium-clear{
background: transparent !important;
background-color: transparent !important;
border-color: transparent !important;
}
.Medium,
.Medium p:first-child{
margin-top:0;
}
.Medium p:last-child{
margin-bottom:0;
}
.Medium p:empty {
display: block;
height: 1em;
}
.Medium-paste-hook{
position: absolute;
opacity:0;
}
.Medium-placeholder{
position: absolute;
width: 100%;
}
.Medium-placeholder div{
-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
filter: alpha(opacity=50);
opacity: 0.5;
position: absolute;
}
.Medium-placeholder-rich div{
font-style: italic;
}
.Medium-inline,
.Medium-placeholder-inline div,
.Medium-inlineRich,
.Medium-placeholder-inlineRich div{
overflow: hidden;
white-space: nowrap;
}

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
# encoding: utf-8 # encoding: utf-8
require 'browser_test_helper' require 'browser_test_helper'
class AgentTicketActionLevel5Test < TestCase class AgentTicketActionLevel0Test < TestCase
def test_I def test_I
random = 'text_module_test_' + rand(99999999).to_s random = 'text_module_test_' + rand(99999999).to_s
random2 = 'text_module_test_' + rand(99999999).to_s random2 = 'text_module_test_' + rand(99999999).to_s
@ -122,12 +122,12 @@ class AgentTicketActionLevel5Test < TestCase
}, },
{ {
:execute => 'watch_for', :execute => 'watch_for',
:area => '.active [data-name="body"]', :area => '.active div[data-name=body]',
:value => '', :value => '',
}, },
{ {
:execute => 'wait', :execute => 'wait',
:value => 1, :value => 3,
}, },
{ {
:execute => 'set_ticket_attributes', :execute => 'set_ticket_attributes',
@ -156,7 +156,7 @@ class AgentTicketActionLevel5Test < TestCase
}, },
{ {
:execute => 'match', :execute => 'match',
:css => '.active [data-name="body"]', :css => '.active div[data-name=body]',
:value => 'some content' + random, :value => 'some content' + random,
:match_result => true, :match_result => true,
}, },
@ -313,10 +313,13 @@ class AgentTicketActionLevel5Test < TestCase
:execute => 'wait', :execute => 'wait',
:value => 1, :value => 1,
}, },
# instance.execute_script( '$(".content.active div[data-name=body]").focus()' )
#execute] == 'js'
# result = instance.execute_script( action[:value
{ {
:where => :instance2, :where => :instance2,
:execute => 'match', :execute => 'match',
:css => '.active [data-name="body"]', :css => '.active div[data-name=body]',
:value => 'some content ' + random, :value => 'some content ' + random,
:match_result => true, :match_result => true,
}, },
@ -382,7 +385,7 @@ class AgentTicketActionLevel5Test < TestCase
{ {
:where => :instance2, :where => :instance2,
:execute => 'match', :execute => 'match',
:css => '.active [data-name="body"]', :css => '.active div[data-name=body]',
:value => 'some content Braun' + random, :value => 'some content Braun' + random,
:match_result => true, :match_result => true,
}, },
@ -460,7 +463,7 @@ class AgentTicketActionLevel5Test < TestCase
{ {
:where => :instance2, :where => :instance2,
:execute => 'match', :execute => 'match',
:css => '.active [data-name="body"]', :css => '.active div[data-name=body]',
:value => 'some content Braun' + random, :value => 'some content Braun' + random,
:match_result => true, :match_result => true,
}, },
@ -561,7 +564,7 @@ class AgentTicketActionLevel5Test < TestCase
{ {
:where => :instance2, :where => :instance2,
:execute => 'match', :execute => 'match',
:css => '.active [data-name="body"]', :css => '.active div[data-name=body]',
:value => 'some content ' + lastname, :value => 'some content ' + lastname,
:match_result => true, :match_result => true,
}, },

View file

@ -104,7 +104,7 @@ class AgentTicketActionsLevel3Test < TestCase
}, },
{ {
:execute => 'wait', :execute => 'wait',
:value => 5, :value => 6,
}, },
{ {
:where => :instance2, :where => :instance2,
@ -155,6 +155,10 @@ class AgentTicketActionsLevel3Test < TestCase
}, },
# check edit screen in instance 2 # check edit screen in instance 2
{
:execute => 'wait',
:value => 1,
},
{ {
:where => :instance2, :where => :instance2,
:execute => 'verify_ticket_attributes', :execute => 'verify_ticket_attributes',

View file

@ -21,7 +21,7 @@ class AgentTicketActionLevel4Test < TestCase
}, },
{ {
:execute => 'wait', :execute => 'wait',
:value => 6, :value => 10,
}, },
# reload instances, verify autosave # reload instances, verify autosave

View file

@ -45,7 +45,7 @@ class CustomerTicketCreateTest < TestCase
}, },
{ {
:execute => 'wait', :execute => 'wait',
:value => 4, :value => 5,
}, },
{ {
:execute => 'check', :execute => 'check',

View file

@ -378,14 +378,17 @@ class TestCase < Test::Unit::TestCase
# }, # },
end end
if action[:body] if action[:body]
element = instance.find_element( { :css => '.content.active [data-name="body"]' } ) instance.execute_script( '$(".content.active div[data-name=body]").focus()' )
sleep 0.5
element = instance.find_element( { :css => '.content.active div[data-name=body]' } )
element.clear element.clear
element.send_keys( action[:body] ) element.send_keys( action[:body] )
# check if body is filled / in case use workaround # check if body is filled / in case use workaround
body = element.text body = element.text
#puts "body '#{body}'" #puts "body '#{body}'"
if !body || body.empty? || body == '' || body == ' ' if !body || body.empty? || body == '' || body == ' '
result = instance.execute_script( '$(".content.active [data-name=body]").text("' + action[:body] + '")' ) puts "DBODY WORKSAROUND"
result = instance.execute_script( '$(".content.active div[data-name=body]").text("' + action[:body] + '").focus()' )
#puts "r #{result.inspect}" #puts "r #{result.inspect}"
end end
end end
@ -426,7 +429,9 @@ class TestCase < Test::Unit::TestCase
sleep 0.2 sleep 0.2
end end
if action[:body] if action[:body]
element = instance.find_element( { :css => '.active .newTicket [data-name="body"]' } ) instance.execute_script( '$(".active .newTicket div[data-name=body]").focus()' )
sleep 0.5
element = instance.find_element( { :css => '.active .newTicket div[data-name=body]' } )
element.clear element.clear
element.send_keys( action[:body] ) element.send_keys( action[:body] )
@ -434,7 +439,8 @@ class TestCase < Test::Unit::TestCase
body = element.text body = element.text
#puts "body '#{body}'" #puts "body '#{body}'"
if !body || body.empty? || body == '' || body == ' ' if !body || body.empty? || body == '' || body == ' '
result = instance.execute_script( '$(".content.active .newTicket [data-name=body]").text("' + action[:body] + '").focus()' ) puts "DBODY WORKSAROUND"
result = instance.execute_script( '$(".active .newTicket div[data-name=body]").text("' + action[:body] + '").focus()' )
#puts "r #{result.inspect}" #puts "r #{result.inspect}"
end end
end end
@ -444,7 +450,7 @@ class TestCase < Test::Unit::TestCase
end end
sleep 0.8 sleep 0.8
#instance.execute_script( '$(".content.active .newTicket form").submit()' ) #instance.execute_script( '$(".content.active .newTicket form").submit()' )
instance.find_element( { :css => '.content.active .newTicket button.submit' } ).click instance.find_element( { :css => '.active .newTicket button.submit' } ).click
sleep 1 sleep 1
(1..16).each {|loop| (1..16).each {|loop|
if instance.current_url =~ /#{Regexp.quote('#ticket/zoom/')}/ if instance.current_url =~ /#{Regexp.quote('#ticket/zoom/')}/