From 5c2330512ec66017c7213a13748e7eac62fa64b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Mon, 11 Oct 2021 13:48:50 +0800 Subject: [PATCH 01/28] =?UTF-8?q?@=E6=88=91=EF=BC=9A=E5=9C=A8markdown?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=99=A8=E4=B8=AD=E8=BE=93=E5=85=A5@?= =?UTF-8?q?=E6=88=91=E5=BC=B9=E5=87=BA=E5=88=97=E8=A1=A8=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E5=BC=95=E5=85=A5=E7=9A=84js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/jquery.atwho.min.css | 1 + public/index.html | 4 + public/js/jquery.atwho.js | 1215 +++++++++++++++++++++++++++++++ public/js/jquery.atwho.min.js | 1 + public/js/jquery.caret.js | 436 +++++++++++ public/js/jquery.caret.min.js | 2 + 6 files changed, 1659 insertions(+) create mode 100644 public/css/jquery.atwho.min.css create mode 100644 public/js/jquery.atwho.js create mode 100644 public/js/jquery.atwho.min.js create mode 100644 public/js/jquery.caret.js create mode 100644 public/js/jquery.caret.min.js diff --git a/public/css/jquery.atwho.min.css b/public/css/jquery.atwho.min.css new file mode 100644 index 00000000..f770dc73 --- /dev/null +++ b/public/css/jquery.atwho.min.css @@ -0,0 +1 @@ +.atwho-view{position:absolute;top:0;left:0;display:none;margin-top:18px;background:#fff;color:#000;border:1px solid #DDD;border-radius:3px;box-shadow:0 0 5px rgba(0,0,0,.1);min-width:120px;z-index:11110!important}.atwho-view .atwho-header{padding:5px;margin:5px;cursor:pointer;border-bottom:solid 1px #eaeff1;color:#6f8092;font-size:11px;font-weight:700}.atwho-view .atwho-header .small{color:#6f8092;float:right;padding-top:2px;margin-right:-5px;font-size:12px;font-weight:400}.atwho-view .atwho-header:hover{cursor:default}.atwho-view .cur{background:#36F;color:#fff}.atwho-view .cur small{color:#fff}.atwho-view strong{color:#36F}.atwho-view .cur strong{color:#fff;font:700}.atwho-view ul{list-style:none;padding:0;margin:auto;max-height:200px;overflow-y:auto}.atwho-view ul li{display:block;padding:5px 10px;border-bottom:1px solid #DDD;cursor:pointer}.atwho-view small{font-size:smaller;color:#777;font-weight:400} \ No newline at end of file diff --git a/public/index.html b/public/index.html index 9489fe42..fe0ce393 100755 --- a/public/index.html +++ b/public/index.html @@ -13,6 +13,7 @@ + <%= htmlWebpackPlugin.tags.headTags %> @@ -25,6 +26,9 @@ + + + <%= htmlWebpackPlugin.tags.bodyTags %> \ No newline at end of file diff --git a/public/js/jquery.atwho.js b/public/js/jquery.atwho.js new file mode 100644 index 00000000..09ca40c1 --- /dev/null +++ b/public/js/jquery.atwho.js @@ -0,0 +1,1215 @@ +/** + * at.js - 1.5.4 + * Copyright (c) 2018 chord.luo ; + * Homepage: http://ichord.github.com/At.js + * License: MIT + */ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module unless amdModuleId is set + define(["jquery"], function (a0) { + return (factory(a0)); + }); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(require("jquery")); + } else { + factory(jQuery); + } +}(this, function ($) { +var DEFAULT_CALLBACKS, KEY_CODE; + +KEY_CODE = { + ESC: 27, + TAB: 9, + ENTER: 13, + CTRL: 17, + A: 65, + P: 80, + N: 78, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + BACKSPACE: 8, + SPACE: 32 +}; + +DEFAULT_CALLBACKS = { + beforeSave: function(data) { + return Controller.arrayToDefaultHash(data); + }, + matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) { + var _a, _y, match, regexp, space; + flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + if (should_startWithSpace) { + flag = '(?:^|\\s)' + flag; + } + _a = decodeURI("%C3%80"); + _y = decodeURI("%C3%BF"); + space = acceptSpaceBar ? "\ " : ""; + regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_" + space + "\'\.\+\-]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi'); + match = regexp.exec(subtext); + if (match) { + return match[2] || match[1]; + } else { + return null; + } + }, + filter: function(query, data, searchKey) { + var _results, i, item, len; + _results = []; + for (i = 0, len = data.length; i < len; i++) { + item = data[i]; + if (~new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase())) { + _results.push(item); + } + } + return _results; + }, + remoteFilter: null, + sorter: function(query, items, searchKey) { + var _results, i, item, len; + if (!query) { + return items; + } + _results = []; + for (i = 0, len = items.length; i < len; i++) { + item = items[i]; + item.atwho_order = new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase()); + if (item.atwho_order > -1) { + _results.push(item); + } + } + return _results.sort(function(a, b) { + return a.atwho_order - b.atwho_order; + }); + }, + tplEval: function(tpl, map) { + var error, error1, template; + template = tpl; + try { + if (typeof tpl !== 'string') { + template = tpl(map); + } + return template.replace(/\$\{([^\}]*)\}/g, function(tag, key, pos) { + return map[key]; + }); + } catch (error1) { + error = error1; + return ""; + } + }, + highlighter: function(li, query) { + var regexp; + if (!query) { + return li; + } + regexp = new RegExp(">\\s*([^\<]*?)(" + query.replace("+", "\\+") + ")([^\<]*)\\s*<", 'ig'); + return li.replace(regexp, function(str, $1, $2, $3) { + return '> ' + $1 + '' + $2 + '' + $3 + ' <'; + }); + }, + beforeInsert: function(value, $li, e) { + return value; + }, + beforeReposition: function(offset) { + return offset; + }, + afterMatchFailed: function(at, el) {} +}; + +var App; + +App = (function() { + function App(inputor) { + this.currentFlag = null; + this.controllers = {}; + this.aliasMaps = {}; + this.$inputor = $(inputor); + this.setupRootElement(); + this.listen(); + } + + App.prototype.createContainer = function(doc) { + var ref; + if ((ref = this.$el) != null) { + ref.remove(); + } + return $(doc.body).append(this.$el = $("
")); + }; + + App.prototype.setupRootElement = function(iframe, asRoot) { + var error, error1; + if (asRoot == null) { + asRoot = false; + } + if (iframe) { + this.window = iframe.contentWindow; + this.document = iframe.contentDocument || this.window.document; + this.iframe = iframe; + } else { + this.document = this.$inputor[0].ownerDocument; + this.window = this.document.defaultView || this.document.parentWindow; + try { + this.iframe = this.window.frameElement; + } catch (error1) { + error = error1; + this.iframe = null; + if ($.fn.atwho.debug) { + throw new Error("iframe auto-discovery is failed.\nPlease use `setIframe` to set the target iframe manually.\n" + error); + } + } + } + return this.createContainer((this.iframeAsRoot = asRoot) ? this.document : document); + }; + + App.prototype.controller = function(at) { + var c, current, currentFlag, ref; + if (this.aliasMaps[at]) { + current = this.controllers[this.aliasMaps[at]]; + } else { + ref = this.controllers; + for (currentFlag in ref) { + c = ref[currentFlag]; + if (currentFlag === at) { + current = c; + break; + } + } + } + if (current) { + return current; + } else { + return this.controllers[this.currentFlag]; + } + }; + + App.prototype.setContextFor = function(at) { + this.currentFlag = at; + return this; + }; + + App.prototype.reg = function(flag, setting) { + var base, controller; + controller = (base = this.controllers)[flag] || (base[flag] = this.$inputor.is('[contentEditable]') ? new EditableController(this, flag) : new TextareaController(this, flag)); + if (setting.alias) { + this.aliasMaps[setting.alias] = flag; + } + controller.init(setting); + return this; + }; + + App.prototype.listen = function() { + return this.$inputor.on('compositionstart', (function(_this) { + return function(e) { + var ref; + if ((ref = _this.controller()) != null) { + ref.view.hide(); + } + _this.isComposing = true; + return null; + }; + })(this)).on('compositionend', (function(_this) { + return function(e) { + _this.isComposing = false; + setTimeout(function(e) { + return _this.dispatch(e); + }); + return null; + }; + })(this)).on('keyup.atwhoInner', (function(_this) { + return function(e) { + return _this.onKeyup(e); + }; + })(this)).on('keydown.atwhoInner', (function(_this) { + return function(e) { + return _this.onKeydown(e); + }; + })(this)).on('blur.atwhoInner', (function(_this) { + return function(e) { + var c; + if (c = _this.controller()) { + c.expectedQueryCBId = null; + return c.view.hide(e, c.getOpt("displayTimeout")); + } + }; + })(this)).on('click.atwhoInner', (function(_this) { + return function(e) { + return _this.dispatch(e); + }; + })(this)).on('scroll.atwhoInner', (function(_this) { + return function() { + var lastScrollTop; + lastScrollTop = _this.$inputor.scrollTop(); + return function(e) { + var currentScrollTop, ref; + currentScrollTop = e.target.scrollTop; + if (lastScrollTop !== currentScrollTop) { + if ((ref = _this.controller()) != null) { + ref.view.hide(e); + } + } + lastScrollTop = currentScrollTop; + return true; + }; + }; + })(this)()); + }; + + App.prototype.shutdown = function() { + var _, c, ref; + ref = this.controllers; + for (_ in ref) { + c = ref[_]; + c.destroy(); + delete this.controllers[_]; + } + this.$inputor.off('.atwhoInner'); + return this.$el.remove(); + }; + + App.prototype.dispatch = function(e) { + var _, c, ref, results; + if (void 0 === e) { + return; + } + ref = this.controllers; + results = []; + for (_ in ref) { + c = ref[_]; + results.push(c.lookUp(e)); + } + return results; + }; + + App.prototype.onKeyup = function(e) { + var ref; + switch (e.keyCode) { + case KEY_CODE.ESC: + e.preventDefault(); + if ((ref = this.controller()) != null) { + ref.view.hide(); + } + break; + case KEY_CODE.DOWN: + case KEY_CODE.UP: + case KEY_CODE.CTRL: + case KEY_CODE.ENTER: + $.noop(); + break; + case KEY_CODE.P: + case KEY_CODE.N: + if (!e.ctrlKey) { + this.dispatch(e); + } + break; + default: + this.dispatch(e); + } + }; + + App.prototype.onKeydown = function(e) { + var ref, view; + view = (ref = this.controller()) != null ? ref.view : void 0; + if (!(view && view.visible())) { + return; + } + switch (e.keyCode) { + case KEY_CODE.ESC: + e.preventDefault(); + view.hide(e); + break; + case KEY_CODE.UP: + e.preventDefault(); + view.prev(); + break; + case KEY_CODE.DOWN: + e.preventDefault(); + view.next(); + break; + case KEY_CODE.P: + if (!e.ctrlKey) { + return; + } + e.preventDefault(); + view.prev(); + break; + case KEY_CODE.N: + if (!e.ctrlKey) { + return; + } + e.preventDefault(); + view.next(); + break; + case KEY_CODE.TAB: + case KEY_CODE.ENTER: + case KEY_CODE.SPACE: + if (!view.visible()) { + return; + } + if (!this.controller().getOpt('spaceSelectsMatch') && e.keyCode === KEY_CODE.SPACE) { + return; + } + if (!this.controller().getOpt('tabSelectsMatch') && e.keyCode === KEY_CODE.TAB) { + return; + } + if (view.highlighted()) { + e.preventDefault(); + view.choose(e); + } else { + view.hide(e); + } + break; + default: + $.noop(); + } + }; + + return App; + +})(); + +var Controller, + slice = [].slice; + +Controller = (function() { + Controller.prototype.uid = function() { + return (Math.random().toString(16) + "000000000").substr(2, 8) + (new Date().getTime()); + }; + + function Controller(app, at1) { + this.app = app; + this.at = at1; + this.$inputor = this.app.$inputor; + this.id = this.$inputor[0].id || this.uid(); + this.expectedQueryCBId = null; + this.setting = null; + this.query = null; + this.pos = 0; + this.range = null; + if ((this.$el = $("#atwho-ground-" + this.id, this.app.$el)).length === 0) { + this.app.$el.append(this.$el = $("
")); + } + this.model = new Model(this); + this.view = new View(this); + } + + Controller.prototype.init = function(setting) { + this.setting = $.extend({}, this.setting || $.fn.atwho["default"], setting); + this.view.init(); + return this.model.reload(this.setting.data); + }; + + Controller.prototype.destroy = function() { + this.trigger('beforeDestroy'); + this.model.destroy(); + this.view.destroy(); + return this.$el.remove(); + }; + + Controller.prototype.callDefault = function() { + var args, error, error1, funcName; + funcName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; + try { + return DEFAULT_CALLBACKS[funcName].apply(this, args); + } catch (error1) { + error = error1; + return $.error(error + " Or maybe At.js doesn't have function " + funcName); + } + }; + + Controller.prototype.trigger = function(name, data) { + var alias, eventName; + if (data == null) { + data = []; + } + data.push(this); + alias = this.getOpt('alias'); + eventName = alias ? name + "-" + alias + ".atwho" : name + ".atwho"; + return this.$inputor.trigger(eventName, data); + }; + + Controller.prototype.callbacks = function(funcName) { + return this.getOpt("callbacks")[funcName] || DEFAULT_CALLBACKS[funcName]; + }; + + Controller.prototype.getOpt = function(at, default_value) { + var e, error1; + try { + return this.setting[at]; + } catch (error1) { + e = error1; + return null; + } + }; + + Controller.prototype.insertContentFor = function($li) { + var data, tpl; + tpl = this.getOpt('insertTpl'); + data = $.extend({}, $li.data('item-data'), { + 'atwho-at': this.at + }); + return this.callbacks("tplEval").call(this, tpl, data, "onInsert"); + }; + + Controller.prototype.renderView = function(data) { + var searchKey; + searchKey = this.getOpt("searchKey"); + data = this.callbacks("sorter").call(this, this.query.text, data.slice(0, 1001), searchKey); + return this.view.render(data.slice(0, this.getOpt('limit'))); + }; + + Controller.arrayToDefaultHash = function(data) { + var i, item, len, results; + if (!$.isArray(data)) { + return data; + } + results = []; + for (i = 0, len = data.length; i < len; i++) { + item = data[i]; + if ($.isPlainObject(item)) { + results.push(item); + } else { + results.push({ + name: item + }); + } + } + return results; + }; + + Controller.prototype.lookUp = function(e) { + var query, wait; + if (e && e.type === 'click' && !this.getOpt('lookUpOnClick')) { + return; + } + if (this.getOpt('suspendOnComposing') && this.app.isComposing) { + return; + } + query = this.catchQuery(e); + if (!query) { + this.expectedQueryCBId = null; + return query; + } + this.app.setContextFor(this.at); + if (wait = this.getOpt('delay')) { + this._delayLookUp(query, wait); + } else { + this._lookUp(query); + } + return query; + }; + + Controller.prototype._delayLookUp = function(query, wait) { + var now, remaining; + now = Date.now ? Date.now() : new Date().getTime(); + this.previousCallTime || (this.previousCallTime = now); + remaining = wait - (now - this.previousCallTime); + if ((0 < remaining && remaining < wait)) { + this.previousCallTime = now; + this._stopDelayedCall(); + return this.delayedCallTimeout = setTimeout((function(_this) { + return function() { + _this.previousCallTime = 0; + _this.delayedCallTimeout = null; + return _this._lookUp(query); + }; + })(this), wait); + } else { + this._stopDelayedCall(); + if (this.previousCallTime !== now) { + this.previousCallTime = 0; + } + return this._lookUp(query); + } + }; + + Controller.prototype._stopDelayedCall = function() { + if (this.delayedCallTimeout) { + clearTimeout(this.delayedCallTimeout); + return this.delayedCallTimeout = null; + } + }; + + Controller.prototype._generateQueryCBId = function() { + return {}; + }; + + Controller.prototype._lookUp = function(query) { + var _callback; + _callback = function(queryCBId, data) { + if (queryCBId !== this.expectedQueryCBId) { + return; + } + if (data && data.length > 0) { + return this.renderView(this.constructor.arrayToDefaultHash(data)); + } else { + return this.view.hide(); + } + }; + this.expectedQueryCBId = this._generateQueryCBId(); + return this.model.query(query.text, $.proxy(_callback, this, this.expectedQueryCBId)); + }; + + return Controller; + +})(); + +var TextareaController, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +TextareaController = (function(superClass) { + extend(TextareaController, superClass); + + function TextareaController() { + return TextareaController.__super__.constructor.apply(this, arguments); + } + + TextareaController.prototype.catchQuery = function() { + var caretPos, content, end, isString, query, start, subtext; + content = this.$inputor.val(); + caretPos = this.$inputor.caret('pos', { + iframe: this.app.iframe + }); + subtext = content.slice(0, caretPos); + query = this.callbacks("matcher").call(this, this.at, subtext, this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar")); + isString = typeof query === 'string'; + if (isString && query.length < this.getOpt('minLen', 0)) { + return; + } + if (isString && query.length <= this.getOpt('maxLen', 20)) { + start = caretPos - query.length; + end = start + query.length; + this.pos = start; + query = { + 'text': query, + 'headPos': start, + 'endPos': end + }; + this.trigger("matched", [this.at, query.text]); + } else { + query = null; + this.view.hide(); + } + return this.query = query; + }; + + TextareaController.prototype.rect = function() { + var c, iframeOffset, scaleBottom; + if (!(c = this.$inputor.caret('offset', this.pos - 1, { + iframe: this.app.iframe + }))) { + return; + } + if (this.app.iframe && !this.app.iframeAsRoot) { + iframeOffset = $(this.app.iframe).offset(); + c.left += iframeOffset.left; + c.top += iframeOffset.top; + } + scaleBottom = this.app.document.selection ? 0 : 2; + return { + left: c.left, + top: c.top, + bottom: c.top + c.height + scaleBottom + }; + }; + + TextareaController.prototype.insert = function(content, $li) { + var $inputor, source, startStr, suffix, text; + $inputor = this.$inputor; + source = $inputor.val(); + startStr = source.slice(0, Math.max(this.query.headPos - this.at.length, 0)); + suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || " "; + content += suffix; + text = "" + startStr + content + (source.slice(this.query['endPos'] || 0)); + $inputor.val(text); + $inputor.caret('pos', startStr.length + content.length, { + iframe: this.app.iframe + }); + if (!$inputor.is(':focus')) { + $inputor.focus(); + } + return $inputor.change(); + }; + + return TextareaController; + +})(Controller); + +var EditableController, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +EditableController = (function(superClass) { + extend(EditableController, superClass); + + function EditableController() { + return EditableController.__super__.constructor.apply(this, arguments); + } + + EditableController.prototype._getRange = function() { + var sel; + sel = this.app.window.getSelection(); + if (sel.rangeCount > 0) { + return sel.getRangeAt(0); + } + }; + + EditableController.prototype._setRange = function(position, node, range) { + if (range == null) { + range = this._getRange(); + } + if (!(range && node)) { + return; + } + node = $(node)[0]; + if (position === 'after') { + range.setEndAfter(node); + range.setStartAfter(node); + } else { + range.setEndBefore(node); + range.setStartBefore(node); + } + range.collapse(false); + return this._clearRange(range); + }; + + EditableController.prototype._clearRange = function(range) { + var sel; + if (range == null) { + range = this._getRange(); + } + sel = this.app.window.getSelection(); + if (this.ctrl_a_pressed == null) { + sel.removeAllRanges(); + return sel.addRange(range); + } + }; + + EditableController.prototype._movingEvent = function(e) { + var ref; + return e.type === 'click' || ((ref = e.which) === KEY_CODE.RIGHT || ref === KEY_CODE.LEFT || ref === KEY_CODE.UP || ref === KEY_CODE.DOWN); + }; + + EditableController.prototype._unwrap = function(node) { + var next; + node = $(node).unwrap().get(0); + if ((next = node.nextSibling) && next.nodeValue) { + node.nodeValue += next.nodeValue; + $(next).remove(); + } + return node; + }; + + EditableController.prototype.catchQuery = function(e) { + var $inserted, $query, _range, index, inserted, isString, lastNode, matched, offset, query, query_content, range; + if (!(range = this._getRange())) { + return; + } + if (!range.collapsed) { + return; + } + if (e.which === KEY_CODE.ENTER) { + ($query = $(range.startContainer).closest('.atwho-query')).contents().unwrap(); + if ($query.is(':empty')) { + $query.remove(); + } + ($query = $(".atwho-query", this.app.document)).text($query.text()).contents().last().unwrap(); + this._clearRange(); + return; + } + if (/firefox/i.test(navigator.userAgent)) { + if ($(range.startContainer).is(this.$inputor)) { + this._clearRange(); + return; + } + if (e.which === KEY_CODE.BACKSPACE && range.startContainer.nodeType === document.ELEMENT_NODE && (offset = range.startOffset - 1) >= 0) { + _range = range.cloneRange(); + _range.setStart(range.startContainer, offset); + if ($(_range.cloneContents()).contents().last().is('.atwho-inserted')) { + inserted = $(range.startContainer).contents().get(offset); + this._setRange('after', $(inserted).contents().last()); + } + } else if (e.which === KEY_CODE.LEFT && range.startContainer.nodeType === document.TEXT_NODE) { + $inserted = $(range.startContainer.previousSibling); + if ($inserted.is('.atwho-inserted') && range.startOffset === 0) { + this._setRange('after', $inserted.contents().last()); + } + } + } + $(range.startContainer).closest('.atwho-inserted').addClass('atwho-query').siblings().removeClass('atwho-query'); + if (($query = $(".atwho-query", this.app.document)).length > 0 && $query.is(':empty') && $query.text().length === 0) { + $query.remove(); + } + if (!this._movingEvent(e)) { + $query.removeClass('atwho-inserted'); + } + if ($query.length > 0) { + switch (e.which) { + case KEY_CODE.LEFT: + this._setRange('before', $query.get(0), range); + $query.removeClass('atwho-query'); + return; + case KEY_CODE.RIGHT: + this._setRange('after', $query.get(0).nextSibling, range); + $query.removeClass('atwho-query'); + return; + } + } + if ($query.length > 0 && (query_content = $query.attr('data-atwho-at-query'))) { + $query.empty().html(query_content).attr('data-atwho-at-query', null); + this._setRange('after', $query.get(0), range); + } + _range = range.cloneRange(); + _range.setStart(range.startContainer, 0); + matched = this.callbacks("matcher").call(this, this.at, _range.toString(), this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar")); + isString = typeof matched === 'string'; + if ($query.length === 0 && isString && (index = range.startOffset - this.at.length - matched.length) >= 0) { + range.setStart(range.startContainer, index); + $query = $('', this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass('atwho-query'); + range.surroundContents($query.get(0)); + lastNode = $query.contents().last().get(0); + if (lastNode) { + if (/firefox/i.test(navigator.userAgent)) { + range.setStart(lastNode, lastNode.length); + range.setEnd(lastNode, lastNode.length); + this._clearRange(range); + } else { + this._setRange('after', lastNode, range); + } + } + } + if (isString && matched.length < this.getOpt('minLen', 0)) { + return; + } + if (isString && matched.length <= this.getOpt('maxLen', 20)) { + query = { + text: matched, + el: $query + }; + this.trigger("matched", [this.at, query.text]); + return this.query = query; + } else { + this.view.hide(); + this.query = { + el: $query + }; + if ($query.text().indexOf(this.at) >= 0) { + if (this._movingEvent(e) && $query.hasClass('atwho-inserted')) { + $query.removeClass('atwho-query'); + } else if (false !== this.callbacks('afterMatchFailed').call(this, this.at, $query)) { + this._setRange("after", this._unwrap($query.text($query.text()).contents().first())); + } + } + return null; + } + }; + + EditableController.prototype.rect = function() { + var $iframe, iframeOffset, rect; + rect = this.query.el.offset(); + if (!(rect && this.query.el[0].getClientRects().length)) { + return; + } + if (this.app.iframe && !this.app.iframeAsRoot) { + iframeOffset = ($iframe = $(this.app.iframe)).offset(); + rect.left += iframeOffset.left - this.$inputor.scrollLeft(); + rect.top += iframeOffset.top - this.$inputor.scrollTop(); + } + rect.bottom = rect.top + this.query.el.height(); + return rect; + }; + + EditableController.prototype.insert = function(content, $li) { + var data, overrides, range, suffix, suffixNode; + if (!this.$inputor.is(':focus')) { + this.$inputor.focus(); + } + overrides = this.getOpt('functionOverrides'); + if (overrides.insert) { + return overrides.insert.call(this, content, $li); + } + suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || "\u00A0"; + data = $li.data('item-data'); + this.query.el.removeClass('atwho-query').addClass('atwho-inserted').html(content).attr('data-atwho-at-query', "" + data['atwho-at'] + this.query.text).attr('contenteditable', "false"); + if (range = this._getRange()) { + if (this.query.el.length) { + range.setEndAfter(this.query.el[0]); + } + range.collapse(false); + range.insertNode(suffixNode = this.app.document.createTextNode("" + suffix)); + this._setRange('after', suffixNode, range); + } + if (!this.$inputor.is(':focus')) { + this.$inputor.focus(); + } + return this.$inputor.change(); + }; + + return EditableController; + +})(Controller); + +var Model; + +Model = (function() { + function Model(context) { + this.context = context; + this.at = this.context.at; + this.storage = this.context.$inputor; + } + + Model.prototype.destroy = function() { + return this.storage.data(this.at, null); + }; + + Model.prototype.saved = function() { + return this.fetch() > 0; + }; + + Model.prototype.query = function(query, callback) { + var _remoteFilter, data, searchKey; + data = this.fetch(); + searchKey = this.context.getOpt("searchKey"); + data = this.context.callbacks('filter').call(this.context, query, data, searchKey) || []; + _remoteFilter = this.context.callbacks('remoteFilter'); + if (data.length > 0 || (!_remoteFilter && data.length === 0)) { + return callback(data); + } else { + return _remoteFilter.call(this.context, query, callback); + } + }; + + Model.prototype.fetch = function() { + return this.storage.data(this.at) || []; + }; + + Model.prototype.save = function(data) { + return this.storage.data(this.at, this.context.callbacks("beforeSave").call(this.context, data || [])); + }; + + Model.prototype.load = function(data) { + if (!(this.saved() || !data)) { + return this._load(data); + } + }; + + Model.prototype.reload = function(data) { + return this._load(data); + }; + + Model.prototype._load = function(data) { + if (typeof data === "string") { + return $.ajax(data, { + dataType: "json" + }).done((function(_this) { + return function(data) { + return _this.save(data); + }; + })(this)); + } else { + return this.save(data); + } + }; + + return Model; + +})(); + +var View; + +View = (function() { + function View(context) { + this.context = context; + this.$el = $("
    "); + this.$elUl = this.$el.children(); + this.timeoutID = null; + this.context.$el.append(this.$el); + this.bindEvent(); + } + + View.prototype.init = function() { + var header_tpl, id; + id = this.context.getOpt("alias") || this.context.at.charCodeAt(0); + header_tpl = this.context.getOpt("headerTpl"); + if (header_tpl && this.$el.children().length === 1) { + this.$el.prepend(header_tpl); + } + return this.$el.attr({ + 'id': "at-view-" + id + }); + }; + + View.prototype.destroy = function() { + return this.$el.remove(); + }; + + View.prototype.bindEvent = function() { + var $menu, lastCoordX, lastCoordY; + $menu = this.$el.find('ul'); + lastCoordX = 0; + lastCoordY = 0; + return $menu.on('mousemove.atwho-view', 'li', (function(_this) { + return function(e) { + var $cur; + if (lastCoordX === e.clientX && lastCoordY === e.clientY) { + return; + } + lastCoordX = e.clientX; + lastCoordY = e.clientY; + $cur = $(e.currentTarget); + if ($cur.hasClass('cur')) { + return; + } + $menu.find('.cur').removeClass('cur'); + return $cur.addClass('cur'); + }; + })(this)).on('click.atwho-view', 'li', (function(_this) { + return function(e) { + $menu.find('.cur').removeClass('cur'); + $(e.currentTarget).addClass('cur'); + _this.choose(e); + return e.preventDefault(); + }; + })(this)); + }; + + View.prototype.visible = function() { + return $.expr.filters.visible(this.$el[0]); + }; + + View.prototype.highlighted = function() { + return this.$el.find(".cur").length > 0; + }; + + View.prototype.choose = function(e) { + var $li, content; + if (($li = this.$el.find(".cur")).length) { + content = this.context.insertContentFor($li); + this.context._stopDelayedCall(); + this.context.insert(this.context.callbacks("beforeInsert").call(this.context, content, $li, e), $li); + this.context.trigger("inserted", [$li, e]); + this.hide(e); + } + if (this.context.getOpt("hideWithoutSuffix")) { + return this.stopShowing = true; + } + }; + + View.prototype.reposition = function(rect) { + var _window, offset, overflowOffset, ref; + _window = this.context.app.iframeAsRoot ? this.context.app.window : window; + if (rect.bottom + this.$el.height() - $(_window).scrollTop() > $(_window).height()) { + rect.bottom = rect.top - this.$el.height(); + } + if (rect.left > (overflowOffset = $(_window).width() - this.$el.width() - 5)) { + rect.left = overflowOffset; + } + offset = { + left: rect.left, + top: rect.bottom + }; + if ((ref = this.context.callbacks("beforeReposition")) != null) { + ref.call(this.context, offset); + } + this.$el.offset(offset); + return this.context.trigger("reposition", [offset]); + }; + + View.prototype.next = function() { + var cur, next, nextEl, offset; + cur = this.$el.find('.cur').removeClass('cur'); + next = cur.next(); + if (!next.length) { + next = this.$el.find('li:first'); + } + next.addClass('cur'); + nextEl = next[0]; + offset = nextEl.offsetTop + nextEl.offsetHeight + (nextEl.nextSibling ? nextEl.nextSibling.offsetHeight : 0); + return this.scrollTop(Math.max(0, offset - this.$el.height())); + }; + + View.prototype.prev = function() { + var cur, offset, prev, prevEl; + cur = this.$el.find('.cur').removeClass('cur'); + prev = cur.prev(); + if (!prev.length) { + prev = this.$el.find('li:last'); + } + prev.addClass('cur'); + prevEl = prev[0]; + offset = prevEl.offsetTop + prevEl.offsetHeight + (prevEl.nextSibling ? prevEl.nextSibling.offsetHeight : 0); + return this.scrollTop(Math.max(0, offset - this.$el.height())); + }; + + View.prototype.scrollTop = function(scrollTop) { + var scrollDuration; + scrollDuration = this.context.getOpt('scrollDuration'); + if (scrollDuration) { + return this.$elUl.animate({ + scrollTop: scrollTop + }, scrollDuration); + } else { + return this.$elUl.scrollTop(scrollTop); + } + }; + + View.prototype.show = function() { + var rect; + if (this.stopShowing) { + this.stopShowing = false; + return; + } + if (!this.visible()) { + this.$el.show(); + this.$el.scrollTop(0); + this.context.trigger('shown'); + } + if (rect = this.context.rect()) { + return this.reposition(rect); + } + }; + + View.prototype.hide = function(e, time) { + var callback; + if (!this.visible()) { + return; + } + if (isNaN(time)) { + this.$el.hide(); + return this.context.trigger('hidden', [e]); + } else { + callback = (function(_this) { + return function() { + return _this.hide(); + }; + })(this); + clearTimeout(this.timeoutID); + return this.timeoutID = setTimeout(callback, time); + } + }; + + View.prototype.render = function(list) { + var $li, $ul, i, item, len, li, tpl; + if (!($.isArray(list) && list.length > 0)) { + this.hide(); + return; + } + this.$el.find('ul').empty(); + $ul = this.$el.find('ul'); + tpl = this.context.getOpt('displayTpl'); + for (i = 0, len = list.length; i < len; i++) { + item = list[i]; + item = $.extend({}, item, { + 'atwho-at': this.context.at + }); + li = this.context.callbacks("tplEval").call(this.context, tpl, item, "onDisplay"); + $li = $(this.context.callbacks("highlighter").call(this.context, li, this.context.query.text)); + $li.data("item-data", item); + $ul.append($li); + } + this.show(); + if (this.context.getOpt('highlightFirst')) { + return $ul.find("li:first").addClass("cur"); + } + }; + + return View; + +})(); + +var Api; + +Api = { + load: function(at, data) { + var c; + if (c = this.controller(at)) { + return c.model.load(data); + } + }, + isSelecting: function() { + var ref; + return !!((ref = this.controller()) != null ? ref.view.visible() : void 0); + }, + hide: function() { + var ref; + return (ref = this.controller()) != null ? ref.view.hide() : void 0; + }, + reposition: function() { + var c; + if (c = this.controller()) { + return c.view.reposition(c.rect()); + } + }, + setIframe: function(iframe, asRoot) { + this.setupRootElement(iframe, asRoot); + return null; + }, + run: function() { + return this.dispatch(); + }, + destroy: function() { + this.shutdown(); + return this.$inputor.data('atwho', null); + } +}; + +$.fn.atwho = function(method) { + var _args, result; + _args = arguments; + result = null; + this.filter('textarea, input, [contenteditable=""], [contenteditable=true]').each(function() { + var $this, app; + if (!(app = ($this = $(this)).data("atwho"))) { + $this.data('atwho', (app = new App(this))); + } + if (typeof method === 'object' || !method) { + return app.reg(method.at, method); + } else if (Api[method] && app) { + return result = Api[method].apply(app, Array.prototype.slice.call(_args, 1)); + } else { + return $.error("Method " + method + " does not exist on jQuery.atwho"); + } + }); + if (result != null) { + return result; + } else { + return this; + } +}; + +$.fn.atwho["default"] = { + at: void 0, + alias: void 0, + data: null, + displayTpl: "
  • ${name}
  • ", + insertTpl: "${atwho-at}${name}", + headerTpl: null, + callbacks: DEFAULT_CALLBACKS, + functionOverrides: {}, + searchKey: "name", + suffix: void 0, + hideWithoutSuffix: false, + startWithSpace: true, + acceptSpaceBar: false, + highlightFirst: true, + limit: 5, + maxLen: 20, + minLen: 0, + displayTimeout: 300, + delay: null, + spaceSelectsMatch: false, + tabSelectsMatch: true, + editableAtwhoQueryAttrs: {}, + scrollDuration: 150, + suspendOnComposing: true, + lookUpOnClick: true +}; + +$.fn.atwho.debug = false; + +})); diff --git a/public/js/jquery.atwho.min.js b/public/js/jquery.atwho.min.js new file mode 100644 index 00000000..857bb931 --- /dev/null +++ b/public/js/jquery.atwho.min.js @@ -0,0 +1 @@ +!function(t,e){"function"==typeof define&&define.amd?define(["jquery"],function(t){return e(t)}):"object"==typeof exports?module.exports=e(require("jquery")):e(jQuery)}(this,function(t){var e,i;i={ESC:27,TAB:9,ENTER:13,CTRL:17,A:65,P:80,N:78,LEFT:37,UP:38,RIGHT:39,DOWN:40,BACKSPACE:8,SPACE:32},e={beforeSave:function(t){return r.arrayToDefaultHash(t)},matcher:function(t,e,i,n){var r,o,s,a,h;return t=t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&"),i&&(t="(?:^|\\s)"+t),r=decodeURI("%C3%80"),o=decodeURI("%C3%BF"),h=n?" ":"",a=new RegExp(t+"([A-Za-z"+r+"-"+o+"0-9_"+h+"'.+-]*)$|"+t+"([^\\x00-\\xff]*)$","gi"),s=a.exec(e),s?s[2]||s[1]:null},filter:function(t,e,i){var n,r,o,s;for(n=[],r=0,s=e.length;s>r;r++)o=e[r],~new String(o[i]).toLowerCase().indexOf(t.toLowerCase())&&n.push(o);return n},remoteFilter:null,sorter:function(t,e,i){var n,r,o,s;if(!t)return e;for(n=[],r=0,s=e.length;s>r;r++)o=e[r],o.atwho_order=new String(o[i]).toLowerCase().indexOf(t.toLowerCase()),o.atwho_order>-1&&n.push(o);return n.sort(function(t,e){return t.atwho_order-e.atwho_order})},tplEval:function(t,e){var i,n,r;r=t;try{return"string"!=typeof t&&(r=t(e)),r.replace(/\$\{([^\}]*)\}/g,function(t,i,n){return e[i]})}catch(n){return i=n,""}},highlighter:function(t,e){var i;return e?(i=new RegExp(">\\s*([^<]*?)("+e.replace("+","\\+")+")([^<]*)\\s*<","ig"),t.replace(i,function(t,e,i,n){return"> "+e+""+i+""+n+" <"})):t},beforeInsert:function(t,e,i){return t},beforeReposition:function(t){return t},afterMatchFailed:function(t,e){}};var n;n=function(){function e(e){this.currentFlag=null,this.controllers={},this.aliasMaps={},this.$inputor=t(e),this.setupRootElement(),this.listen()}return e.prototype.createContainer=function(e){var i;return null!=(i=this.$el)&&i.remove(),t(e.body).append(this.$el=t("
    "))},e.prototype.setupRootElement=function(e,i){var n,r;if(null==i&&(i=!1),e)this.window=e.contentWindow,this.document=e.contentDocument||this.window.document,this.iframe=e;else{this.document=this.$inputor[0].ownerDocument,this.window=this.document.defaultView||this.document.parentWindow;try{this.iframe=this.window.frameElement}catch(r){if(n=r,this.iframe=null,t.fn.atwho.debug)throw new Error("iframe auto-discovery is failed.\nPlease use `setIframe` to set the target iframe manually.\n"+n)}}return this.createContainer((this.iframeAsRoot=i)?this.document:document)},e.prototype.controller=function(t){var e,i,n,r;if(this.aliasMaps[t])i=this.controllers[this.aliasMaps[t]];else{r=this.controllers;for(n in r)if(e=r[n],n===t){i=e;break}}return i?i:this.controllers[this.currentFlag]},e.prototype.setContextFor=function(t){return this.currentFlag=t,this},e.prototype.reg=function(t,e){var i,n;return n=(i=this.controllers)[t]||(i[t]=this.$inputor.is("[contentEditable]")?new l(this,t):new s(this,t)),e.alias&&(this.aliasMaps[e.alias]=t),n.init(e),this},e.prototype.listen=function(){return this.$inputor.on("compositionstart",function(t){return function(e){var i;return null!=(i=t.controller())&&i.view.hide(),t.isComposing=!0,null}}(this)).on("compositionend",function(t){return function(e){return t.isComposing=!1,setTimeout(function(e){return t.dispatch(e)}),null}}(this)).on("keyup.atwhoInner",function(t){return function(e){return t.onKeyup(e)}}(this)).on("keydown.atwhoInner",function(t){return function(e){return t.onKeydown(e)}}(this)).on("blur.atwhoInner",function(t){return function(e){var i;return(i=t.controller())?(i.expectedQueryCBId=null,i.view.hide(e,i.getOpt("displayTimeout"))):void 0}}(this)).on("click.atwhoInner",function(t){return function(e){return t.dispatch(e)}}(this)).on("scroll.atwhoInner",function(t){return function(){var e;return e=t.$inputor.scrollTop(),function(i){var n,r;return n=i.target.scrollTop,e!==n&&null!=(r=t.controller())&&r.view.hide(i),e=n,!0}}}(this)())},e.prototype.shutdown=function(){var t,e,i;i=this.controllers;for(t in i)e=i[t],e.destroy(),delete this.controllers[t];return this.$inputor.off(".atwhoInner"),this.$el.remove()},e.prototype.dispatch=function(t){var e,i,n,r;if(void 0!==t){n=this.controllers,r=[];for(e in n)i=n[e],r.push(i.lookUp(t));return r}},e.prototype.onKeyup=function(e){var n;switch(e.keyCode){case i.ESC:e.preventDefault(),null!=(n=this.controller())&&n.view.hide();break;case i.DOWN:case i.UP:case i.CTRL:case i.ENTER:t.noop();break;case i.P:case i.N:e.ctrlKey||this.dispatch(e);break;default:this.dispatch(e)}},e.prototype.onKeydown=function(e){var n,r;if(r=null!=(n=this.controller())?n.view:void 0,r&&r.visible())switch(e.keyCode){case i.ESC:e.preventDefault(),r.hide(e);break;case i.UP:e.preventDefault(),r.prev();break;case i.DOWN:e.preventDefault(),r.next();break;case i.P:if(!e.ctrlKey)return;e.preventDefault(),r.prev();break;case i.N:if(!e.ctrlKey)return;e.preventDefault(),r.next();break;case i.TAB:case i.ENTER:case i.SPACE:if(!r.visible())return;if(!this.controller().getOpt("spaceSelectsMatch")&&e.keyCode===i.SPACE)return;if(!this.controller().getOpt("tabSelectsMatch")&&e.keyCode===i.TAB)return;r.highlighted()?(e.preventDefault(),r.choose(e)):r.hide(e);break;default:t.noop()}},e}();var r,o=[].slice;r=function(){function i(e,i){this.app=e,this.at=i,this.$inputor=this.app.$inputor,this.id=this.$inputor[0].id||this.uid(),this.expectedQueryCBId=null,this.setting=null,this.query=null,this.pos=0,this.range=null,0===(this.$el=t("#atwho-ground-"+this.id,this.app.$el)).length&&this.app.$el.append(this.$el=t("
    ")),this.model=new u(this),this.view=new c(this)}return i.prototype.uid=function(){return(Math.random().toString(16)+"000000000").substr(2,8)+(new Date).getTime()},i.prototype.init=function(e){return this.setting=t.extend({},this.setting||t.fn.atwho["default"],e),this.view.init(),this.model.reload(this.setting.data)},i.prototype.destroy=function(){return this.trigger("beforeDestroy"),this.model.destroy(),this.view.destroy(),this.$el.remove()},i.prototype.callDefault=function(){var i,n,r,s;s=arguments[0],i=2<=arguments.length?o.call(arguments,1):[];try{return e[s].apply(this,i)}catch(r){return n=r,t.error(n+" Or maybe At.js doesn't have function "+s)}},i.prototype.trigger=function(t,e){var i,n;return null==e&&(e=[]),e.push(this),i=this.getOpt("alias"),n=i?t+"-"+i+".atwho":t+".atwho",this.$inputor.trigger(n,e)},i.prototype.callbacks=function(t){return this.getOpt("callbacks")[t]||e[t]},i.prototype.getOpt=function(t,e){var i,n;try{return this.setting[t]}catch(n){return i=n,null}},i.prototype.insertContentFor=function(e){var i,n;return n=this.getOpt("insertTpl"),i=t.extend({},e.data("item-data"),{"atwho-at":this.at}),this.callbacks("tplEval").call(this,n,i,"onInsert")},i.prototype.renderView=function(t){var e;return e=this.getOpt("searchKey"),t=this.callbacks("sorter").call(this,this.query.text,t.slice(0,1001),e),this.view.render(t.slice(0,this.getOpt("limit")))},i.arrayToDefaultHash=function(e){var i,n,r,o;if(!t.isArray(e))return e;for(o=[],i=0,r=e.length;r>i;i++)n=e[i],t.isPlainObject(n)?o.push(n):o.push({name:n});return o},i.prototype.lookUp=function(t){var e,i;if((!t||"click"!==t.type||this.getOpt("lookUpOnClick"))&&(!this.getOpt("suspendOnComposing")||!this.app.isComposing))return(e=this.catchQuery(t))?(this.app.setContextFor(this.at),(i=this.getOpt("delay"))?this._delayLookUp(e,i):this._lookUp(e),e):(this.expectedQueryCBId=null,e)},i.prototype._delayLookUp=function(t,e){var i,n;return i=Date.now?Date.now():(new Date).getTime(),this.previousCallTime||(this.previousCallTime=i),n=e-(i-this.previousCallTime),n>0&&e>n?(this.previousCallTime=i,this._stopDelayedCall(),this.delayedCallTimeout=setTimeout(function(e){return function(){return e.previousCallTime=0,e.delayedCallTimeout=null,e._lookUp(t)}}(this),e)):(this._stopDelayedCall(),this.previousCallTime!==i&&(this.previousCallTime=0),this._lookUp(t))},i.prototype._stopDelayedCall=function(){return this.delayedCallTimeout?(clearTimeout(this.delayedCallTimeout),this.delayedCallTimeout=null):void 0},i.prototype._generateQueryCBId=function(){return{}},i.prototype._lookUp=function(e){var i;return i=function(t,e){return t===this.expectedQueryCBId?e&&e.length>0?this.renderView(this.constructor.arrayToDefaultHash(e)):this.view.hide():void 0},this.expectedQueryCBId=this._generateQueryCBId(),this.model.query(e.text,t.proxy(i,this,this.expectedQueryCBId))},i}();var s,a=function(t,e){function i(){this.constructor=t}for(var n in e)h.call(e,n)&&(t[n]=e[n]);return i.prototype=e.prototype,t.prototype=new i,t.__super__=e.prototype,t},h={}.hasOwnProperty;s=function(e){function i(){return i.__super__.constructor.apply(this,arguments)}return a(i,e),i.prototype.catchQuery=function(){var t,e,i,n,r,o,s;return e=this.$inputor.val(),t=this.$inputor.caret("pos",{iframe:this.app.iframe}),s=e.slice(0,t),r=this.callbacks("matcher").call(this,this.at,s,this.getOpt("startWithSpace"),this.getOpt("acceptSpaceBar")),n="string"==typeof r,n&&r.length0?t.getRangeAt(0):void 0},n.prototype._setRange=function(e,i,n){return null==n&&(n=this._getRange()),n&&i?(i=t(i)[0],"after"===e?(n.setEndAfter(i),n.setStartAfter(i)):(n.setEndBefore(i),n.setStartBefore(i)),n.collapse(!1),this._clearRange(n)):void 0},n.prototype._clearRange=function(t){var e;return null==t&&(t=this._getRange()),e=this.app.window.getSelection(),null==this.ctrl_a_pressed?(e.removeAllRanges(),e.addRange(t)):void 0},n.prototype._movingEvent=function(t){var e;return"click"===t.type||(e=t.which)===i.RIGHT||e===i.LEFT||e===i.UP||e===i.DOWN},n.prototype._unwrap=function(e){var i;return e=t(e).unwrap().get(0),(i=e.nextSibling)&&i.nodeValue&&(e.nodeValue+=i.nodeValue,t(i).remove()),e},n.prototype.catchQuery=function(e){var n,r,o,s,a,h,l,u,c,p,f,d;if((d=this._getRange())&&d.collapsed){if(e.which===i.ENTER)return(r=t(d.startContainer).closest(".atwho-query")).contents().unwrap(),r.is(":empty")&&r.remove(),(r=t(".atwho-query",this.app.document)).text(r.text()).contents().last().unwrap(),void this._clearRange();if(/firefox/i.test(navigator.userAgent)){if(t(d.startContainer).is(this.$inputor))return void this._clearRange();e.which===i.BACKSPACE&&d.startContainer.nodeType===document.ELEMENT_NODE&&(c=d.startOffset-1)>=0?(o=d.cloneRange(),o.setStart(d.startContainer,c),t(o.cloneContents()).contents().last().is(".atwho-inserted")&&(a=t(d.startContainer).contents().get(c),this._setRange("after",t(a).contents().last()))):e.which===i.LEFT&&d.startContainer.nodeType===document.TEXT_NODE&&(n=t(d.startContainer.previousSibling),n.is(".atwho-inserted")&&0===d.startOffset&&this._setRange("after",n.contents().last()))}if(t(d.startContainer).closest(".atwho-inserted").addClass("atwho-query").siblings().removeClass("atwho-query"),(r=t(".atwho-query",this.app.document)).length>0&&r.is(":empty")&&0===r.text().length&&r.remove(),this._movingEvent(e)||r.removeClass("atwho-inserted"),r.length>0)switch(e.which){case i.LEFT:return this._setRange("before",r.get(0),d),void r.removeClass("atwho-query");case i.RIGHT:return this._setRange("after",r.get(0).nextSibling,d),void r.removeClass("atwho-query")}if(r.length>0&&(f=r.attr("data-atwho-at-query"))&&(r.empty().html(f).attr("data-atwho-at-query",null),this._setRange("after",r.get(0),d)),o=d.cloneRange(),o.setStart(d.startContainer,0),u=this.callbacks("matcher").call(this,this.at,o.toString(),this.getOpt("startWithSpace"),this.getOpt("acceptSpaceBar")),h="string"==typeof u,0===r.length&&h&&(s=d.startOffset-this.at.length-u.length)>=0&&(d.setStart(d.startContainer,s),r=t("",this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass("atwho-query"),d.surroundContents(r.get(0)),l=r.contents().last().get(0),l&&(/firefox/i.test(navigator.userAgent)?(d.setStart(l,l.length),d.setEnd(l,l.length),this._clearRange(d)):this._setRange("after",l,d))),!(h&&u.length=0&&(this._movingEvent(e)&&r.hasClass("atwho-inserted")?r.removeClass("atwho-query"):!1!==this.callbacks("afterMatchFailed").call(this,this.at,r)&&this._setRange("after",this._unwrap(r.text(r.text()).contents().first()))),null)}},n.prototype.rect=function(){var e,i,n;return n=this.query.el.offset(),n&&this.query.el[0].getClientRects().length?(this.app.iframe&&!this.app.iframeAsRoot&&(i=(e=t(this.app.iframe)).offset(),n.left+=i.left-this.$inputor.scrollLeft(),n.top+=i.top-this.$inputor.scrollTop()),n.bottom=n.top+this.query.el.height(),n):void 0},n.prototype.insert=function(t,e){var i,n,r,o,s;return this.$inputor.is(":focus")||this.$inputor.focus(),n=this.getOpt("functionOverrides"),n.insert?n.insert.call(this,t,e):(o=""===(o=this.getOpt("suffix"))?o:o||" ",i=e.data("item-data"),this.query.el.removeClass("atwho-query").addClass("atwho-inserted").html(t).attr("data-atwho-at-query",""+i["atwho-at"]+this.query.text).attr("contenteditable","false"),(r=this._getRange())&&(this.query.el.length&&r.setEndAfter(this.query.el[0]),r.collapse(!1),r.insertNode(s=this.app.document.createTextNode(""+o)),this._setRange("after",s,r)),this.$inputor.is(":focus")||this.$inputor.focus(),this.$inputor.change())},n}(r);var u;u=function(){function e(t){this.context=t,this.at=this.context.at,this.storage=this.context.$inputor}return e.prototype.destroy=function(){return this.storage.data(this.at,null)},e.prototype.saved=function(){return this.fetch()>0},e.prototype.query=function(t,e){var i,n,r;return n=this.fetch(),r=this.context.getOpt("searchKey"),n=this.context.callbacks("filter").call(this.context,t,n,r)||[],i=this.context.callbacks("remoteFilter"),n.length>0||!i&&0===n.length?e(n):i.call(this.context,t,e)},e.prototype.fetch=function(){return this.storage.data(this.at)||[]},e.prototype.save=function(t){return this.storage.data(this.at,this.context.callbacks("beforeSave").call(this.context,t||[]))},e.prototype.load=function(t){return!this.saved()&&t?this._load(t):void 0},e.prototype.reload=function(t){return this._load(t)},e.prototype._load=function(e){return"string"==typeof e?t.ajax(e,{dataType:"json"}).done(function(t){return function(e){return t.save(e)}}(this)):this.save(e)},e}();var c;c=function(){function e(e){this.context=e,this.$el=t("
      "),this.$elUl=this.$el.children(),this.timeoutID=null,this.context.$el.append(this.$el),this.bindEvent()}return e.prototype.init=function(){var t,e;return e=this.context.getOpt("alias")||this.context.at.charCodeAt(0),t=this.context.getOpt("headerTpl"),t&&1===this.$el.children().length&&this.$el.prepend(t),this.$el.attr({id:"at-view-"+e})},e.prototype.destroy=function(){return this.$el.remove()},e.prototype.bindEvent=function(){var e,i,n;return e=this.$el.find("ul"),i=0,n=0,e.on("mousemove.atwho-view","li",function(r){return function(r){var o;if((i!==r.clientX||n!==r.clientY)&&(i=r.clientX,n=r.clientY,o=t(r.currentTarget),!o.hasClass("cur")))return e.find(".cur").removeClass("cur"),o.addClass("cur")}}(this)).on("click.atwho-view","li",function(i){return function(n){return e.find(".cur").removeClass("cur"),t(n.currentTarget).addClass("cur"),i.choose(n),n.preventDefault()}}(this))},e.prototype.visible=function(){return t.expr.filters.visible(this.$el[0])},e.prototype.highlighted=function(){return this.$el.find(".cur").length>0},e.prototype.choose=function(t){var e,i;return(e=this.$el.find(".cur")).length&&(i=this.context.insertContentFor(e),this.context._stopDelayedCall(),this.context.insert(this.context.callbacks("beforeInsert").call(this.context,i,e,t),e),this.context.trigger("inserted",[e,t]),this.hide(t)),this.context.getOpt("hideWithoutSuffix")?this.stopShowing=!0:void 0},e.prototype.reposition=function(e){var i,n,r,o;return i=this.context.app.iframeAsRoot?this.context.app.window:window,e.bottom+this.$el.height()-t(i).scrollTop()>t(i).height()&&(e.bottom=e.top-this.$el.height()),e.left>(r=t(i).width()-this.$el.width()-5)&&(e.left=r),n={left:e.left,top:e.bottom},null!=(o=this.context.callbacks("beforeReposition"))&&o.call(this.context,n),this.$el.offset(n),this.context.trigger("reposition",[n])},e.prototype.next=function(){var t,e,i,n;return t=this.$el.find(".cur").removeClass("cur"),e=t.next(),e.length||(e=this.$el.find("li:first")),e.addClass("cur"),i=e[0],n=i.offsetTop+i.offsetHeight+(i.nextSibling?i.nextSibling.offsetHeight:0),this.scrollTop(Math.max(0,n-this.$el.height()))},e.prototype.prev=function(){var t,e,i,n;return t=this.$el.find(".cur").removeClass("cur"),i=t.prev(),i.length||(i=this.$el.find("li:last")),i.addClass("cur"),n=i[0],e=n.offsetTop+n.offsetHeight+(n.nextSibling?n.nextSibling.offsetHeight:0),this.scrollTop(Math.max(0,e-this.$el.height()))},e.prototype.scrollTop=function(t){var e;return e=this.context.getOpt("scrollDuration"),e?this.$elUl.animate({scrollTop:t},e):this.$elUl.scrollTop(t)},e.prototype.show=function(){var t;return this.stopShowing?void(this.stopShowing=!1):(this.visible()||(this.$el.show(),this.$el.scrollTop(0),this.context.trigger("shown")),(t=this.context.rect())?this.reposition(t):void 0)},e.prototype.hide=function(t,e){var i;if(this.visible())return isNaN(e)?(this.$el.hide(),this.context.trigger("hidden",[t])):(i=function(t){return function(){return t.hide()}}(this),clearTimeout(this.timeoutID),this.timeoutID=setTimeout(i,e))},e.prototype.render=function(e){var i,n,r,o,s,a,h;if(!(t.isArray(e)&&e.length>0))return void this.hide();for(this.$el.find("ul").empty(),n=this.$el.find("ul"),h=this.context.getOpt("displayTpl"),r=0,s=e.length;s>r;r++)o=e[r],o=t.extend({},o,{"atwho-at":this.context.at}),a=this.context.callbacks("tplEval").call(this.context,h,o,"onDisplay"),i=t(this.context.callbacks("highlighter").call(this.context,a,this.context.query.text)),i.data("item-data",o),n.append(i);return this.show(),this.context.getOpt("highlightFirst")?n.find("li:first").addClass("cur"):void 0},e}();var p;p={load:function(t,e){var i;return(i=this.controller(t))?i.model.load(e):void 0},isSelecting:function(){var t;return!!(null!=(t=this.controller())?t.view.visible():void 0)},hide:function(){var t;return null!=(t=this.controller())?t.view.hide():void 0},reposition:function(){var t;return(t=this.controller())?t.view.reposition(t.rect()):void 0},setIframe:function(t,e){return this.setupRootElement(t,e),null},run:function(){return this.dispatch()},destroy:function(){return this.shutdown(),this.$inputor.data("atwho",null)}},t.fn.atwho=function(e){var i,r;return i=arguments,r=null,this.filter('textarea, input, [contenteditable=""], [contenteditable=true]').each(function(){var o,s;return(s=(o=t(this)).data("atwho"))||o.data("atwho",s=new n(this)),"object"!=typeof e&&e?p[e]&&s?r=p[e].apply(s,Array.prototype.slice.call(i,1)):t.error("Method "+e+" does not exist on jQuery.atwho"):s.reg(e.at,e)}),null!=r?r:this},t.fn.atwho["default"]={at:void 0,alias:void 0,data:null,displayTpl:"
    • ${name}
    • ",insertTpl:"${atwho-at}${name}",headerTpl:null,callbacks:e,functionOverrides:{},searchKey:"name",suffix:void 0,hideWithoutSuffix:!1,startWithSpace:!0,acceptSpaceBar:!1,highlightFirst:!0,limit:5,maxLen:20,minLen:0,displayTimeout:300,delay:null,spaceSelectsMatch:!1,tabSelectsMatch:!0,editableAtwhoQueryAttrs:{},scrollDuration:150,suspendOnComposing:!0,lookUpOnClick:!0},t.fn.atwho.debug=!1}); \ No newline at end of file diff --git a/public/js/jquery.caret.js b/public/js/jquery.caret.js new file mode 100644 index 00000000..811ec63e --- /dev/null +++ b/public/js/jquery.caret.js @@ -0,0 +1,436 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(["jquery"], function ($) { + return (root.returnExportsGlobal = factory($)); + }); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like enviroments that support module.exports, + // like Node. + module.exports = factory(require("jquery")); + } else { + factory(jQuery); + } +}(this, function ($) { + +/* + 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 内的插入符 +只实现了获得插入符在文本框中的位置,我设置 +插入符的位置. +*/ + +"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) { + var fn, found, offset, sel; + if (sel = oWindow.getSelection()) { + offset = 0; + found = false; + (fn = function(pos, parent) { + var node, range, _i, _len, _ref, _results; + _ref = parent.childNodes; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + if (found) { + break; + } + if (node.nodeType === 3) { + if (offset + node.length >= pos) { + found = true; + range = oDocument.createRange(); + range.setStart(node, pos - offset); + sel.removeAllRanges(); + sel.addRange(range); + break; + } else { + _results.push(offset += node.length); + } + } else { + _results.push(fn(pos, node)); + } + } + return _results; + })(pos, this.domInputor); + } + 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, shadowCaret; + if (oWindow.getSelection && (range = this.range())) { + if (range.endOffset - 1 > 0 && range.endContainer !== this.domInputor) { + 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(); + } + if (!offset || (offset != null ? offset.height : void 0) === 0) { + clonedRange = range.cloneRange(); + shadowCaret = $(oDocument.createTextNode("|")); + clonedRange.insertNode(shadowCaret[0]); + clonedRange.selectNode(shadowCaret[0]); + rect = clonedRange.getBoundingClientRect(); + offset = { + height: rect.height, + left: rect.left, + top: rect.top + }; + shadowCaret.remove(); + 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) { + value = value.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g, "
      "); + if (/firefox/i.test(navigator.userAgent)) { + value = value.replace(/\s/g, ' '); + } + return value; + }; + if (pos === void 0) { + pos = this.getPos(); + } + start_range = $inputor.val().slice(0, pos); + end_range = $inputor.val().slice(pos); + html = "" + format(start_range) + ""; + html += "|"; + html += "" + format(end_range) + ""; + 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 = $('
      '); + 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; + +$.fn.caret.apis = methods; + + +})); diff --git a/public/js/jquery.caret.min.js b/public/js/jquery.caret.min.js new file mode 100644 index 00000000..a25584e2 --- /dev/null +++ b/public/js/jquery.caret.min.js @@ -0,0 +1,2 @@ +/*! jquery.caret 2016-02-27 */ +!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(c){return a.returnExportsGlobal=b(c)}):"object"==typeof exports?module.exports=b(require("jquery")):b(jQuery)}(this,function(a){"use strict";var b,c,d,e,f,g,h,i,j,k,l;k="caret",b=function(){function b(a){this.$inputor=a,this.domInputor=this.$inputor[0]}return b.prototype.setPos=function(a){var b,c,d,e;return(e=j.getSelection())&&(d=0,c=!1,(b=function(a,f){var g,i,j,k,l,m;for(l=f.childNodes,m=[],j=0,k=l.length;k>j&&(g=l[j],!c);j++)if(3===g.nodeType){if(d+g.length>=a){c=!0,i=h.createRange(),i.setStart(g,a-d),e.removeAllRanges(),e.addRange(i);break}m.push(d+=g.length)}else m.push(b(a,g));return m})(a,this.domInputor)),this.domInputor},b.prototype.getIEPosition=function(){return this.getPosition()},b.prototype.getPosition=function(){var a,b;return b=this.getOffset(),a=this.$inputor.offset(),b.left-=a.left,b.top-=a.top,b},b.prototype.getOldIEPos=function(){var a,b;return b=h.selection.createRange(),a=h.body.createTextRange(),a.moveToElementText(this.domInputor),a.setEndPoint("EndToEnd",b),a.text.length},b.prototype.getPos=function(){var a,b,c;return(c=this.range())?(a=c.cloneRange(),a.selectNodeContents(this.domInputor),a.setEnd(c.endContainer,c.endOffset),b=a.toString().length,a.detach(),b):h.selection?this.getOldIEPos():void 0},b.prototype.getOldIEOffset=function(){var a,b;return a=h.selection.createRange().duplicate(),a.moveStart("character",-1),b=a.getBoundingClientRect(),{height:b.bottom-b.top,left:b.left,top:b.top}},b.prototype.getOffset=function(){var b,c,d,e,f;return j.getSelection&&(d=this.range())?(d.endOffset-1>0&&d.endContainer!==this.domInputor&&(b=d.cloneRange(),b.setStart(d.endContainer,d.endOffset-1),b.setEnd(d.endContainer,d.endOffset),e=b.getBoundingClientRect(),c={height:e.height,left:e.left+e.width,top:e.top},b.detach()),c&&0!==(null!=c?c.height:void 0)||(b=d.cloneRange(),f=a(h.createTextNode("|")),b.insertNode(f[0]),b.selectNode(f[0]),e=b.getBoundingClientRect(),c={height:e.height,left:e.left,top:e.top},f.remove(),b.detach())):h.selection&&(c=this.getOldIEOffset()),c&&(c.top+=a(j).scrollTop(),c.left+=a(j).scrollLeft()),c},b.prototype.range=function(){var a;if(j.getSelection)return a=j.getSelection(),a.rangeCount>0?a.getRangeAt(0):null},b}(),c=function(){function b(a){this.$inputor=a,this.domInputor=this.$inputor[0]}return b.prototype.getIEPos=function(){var a,b,c,d,e,f,g;return b=this.domInputor,f=h.selection.createRange(),e=0,f&&f.parentElement()===b&&(d=b.value.replace(/\r\n/g,"\n"),c=d.length,g=b.createTextRange(),g.moveToBookmark(f.getBookmark()),a=b.createTextRange(),a.collapse(!1),e=g.compareEndPoints("StartToEnd",a)>-1?c:-g.moveStart("character",-c)),e},b.prototype.getPos=function(){return h.selection?this.getIEPos():this.domInputor.selectionStart},b.prototype.setPos=function(a){var b,c;return b=this.domInputor,h.selection?(c=b.createTextRange(),c.move("character",a),c.select()):b.setSelectionRange&&b.setSelectionRange(a,a),b},b.prototype.getIEOffset=function(a){var b,c,d,e;return c=this.domInputor.createTextRange(),a||(a=this.getPos()),c.move("character",a),d=c.boundingLeft,e=c.boundingTop,b=c.boundingHeight,{left:d,top:e,height:b}},b.prototype.getOffset=function(b){var c,d,e;return c=this.$inputor,h.selection?(d=this.getIEOffset(b),d.top+=a(j).scrollTop()+c.scrollTop(),d.left+=a(j).scrollLeft()+c.scrollLeft(),d):(d=c.offset(),e=this.getPosition(b),d={left:d.left+e.left-c.scrollLeft(),top:d.top+e.top-c.scrollTop(),height:e.height})},b.prototype.getPosition=function(a){var b,c,e,f,g,h,i;return b=this.$inputor,f=function(a){return a=a.replace(/<|>|`|"|&/g,"?").replace(/\r\n|\r|\n/g,"
      "),/firefox/i.test(navigator.userAgent)&&(a=a.replace(/\s/g," ")),a},void 0===a&&(a=this.getPos()),i=b.val().slice(0,a),e=b.val().slice(a),g=""+f(i)+"",g+="|",g+=""+f(e)+"",h=new d(b),c=h.create(g).rect()},b.prototype.getIEPosition=function(a){var b,c,d,e,f;return d=this.getIEOffset(a),c=this.$inputor.offset(),e=d.left-c.left,f=d.top-c.top,b=d.height,{left:e,top:f,height:b}},b}(),d=function(){function b(a){this.$inputor=a}return b.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"],b.prototype.mirrorCss=function(){var b,c=this;return b={position:"absolute",left:-9999,top:0,zIndex:-2e4},"TEXTAREA"===this.$inputor.prop("tagName")&&this.css_attr.push("width"),a.each(this.css_attr,function(a,d){return b[d]=c.$inputor.css(d)}),b},b.prototype.create=function(b){return this.$mirror=a("
      "),this.$mirror.css(this.mirrorCss()),this.$mirror.html(b),this.$inputor.after(this.$mirror),this},b.prototype.rect=function(){var a,b,c;return a=this.$mirror.find("#caret"),b=a.position(),c={left:b.left,top:b.top,height:a.height()},this.$mirror.remove(),c},b}(),e={contentEditable:function(a){return!(!a[0].contentEditable||"true"!==a[0].contentEditable)}},g={pos:function(a){return a||0===a?this.setPos(a):this.getPos()},position:function(a){return h.selection?this.getIEPosition(a):this.getPosition(a)},offset:function(a){var b;return b=this.getOffset(a)}},h=null,j=null,i=null,l=function(a){var b;return(b=null!=a?a.iframe:void 0)?(i=b,j=b.contentWindow,h=b.contentDocument||j.document):(i=void 0,j=window,h=document)},f=function(a){var b;h=a[0].ownerDocument,j=h.defaultView||h.parentWindow;try{return i=j.frameElement}catch(c){b=c}},a.fn.caret=function(d,f,h){var i;return g[d]?(a.isPlainObject(f)?(l(f),f=void 0):l(h),i=e.contentEditable(this)?new b(this):new c(this),g[d].apply(i,[f])):a.error("Method "+d+" does not exist on jQuery.caret")},a.fn.caret.EditableCaret=b,a.fn.caret.InputCaret=c,a.fn.caret.Utils=e,a.fn.caret.apis=g}); \ No newline at end of file From 999a15761dab54a6920404675c9482c97c5d23c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Tue, 12 Oct 2021 14:24:18 +0800 Subject: [PATCH 02/28] =?UTF-8?q?md@=E8=B0=81=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/index.html | 6 +++--- src/modules/tpm/challengesnew/tpm-md-editor.js | 12 +++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/public/index.html b/public/index.html index fe0ce393..7774b2c5 100755 --- a/public/index.html +++ b/public/index.html @@ -13,7 +13,7 @@ - + <%= htmlWebpackPlugin.tags.headTags %> @@ -27,8 +27,8 @@ - - + + <%= htmlWebpackPlugin.tags.bodyTags %> \ No newline at end of file diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index e1c06eda..c074d3dc 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -6,6 +6,7 @@ import ResizeObserver from 'resize-observer-polyfill'; import '../../courses/css/Courses.css'; import './css/TPMchallengesnew.css'; import 'codemirror/lib/codemirror.css'; +import { AutoComplete } from 'antd'; const $ = window.$ const mdIcons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "inline-latex", "latex", '|', "image", "table", '|', "line-break", "watch", "clear"]; @@ -200,6 +201,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla } if (onChange) { editorInstance.cm.on('change', (cm) => { + $(`#editorBodyId`).atwho({ + at: '@', + data:['111','222'] + }) // if(forMember){ // document.onkeydown = (e) => { // if (e.key === "@") { @@ -225,6 +230,11 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla useEffect(() => { if (editorInstance && initValue !== undefined) { + $(`#editorBodyId`).atwho({ + at: '@', + data:['111','222'] + }) + const val = editorInstance.getValue(); if (initValue !== null && initValue !== editorInstance.getValue()) { editorInstance.setValue(initValue.toString()) } @@ -273,7 +283,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
      - +
      From 85ef26f70e3b7c3b703aab0bc02850ee4ec097bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Wed, 13 Oct 2021 10:07:38 +0800 Subject: [PATCH 03/28] @who --- .../tpm/challengesnew/css/newquestion.css | 9 +++++ .../tpm/challengesnew/tpm-md-editor.js | 39 +++++++++++-------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/modules/tpm/challengesnew/css/newquestion.css b/src/modules/tpm/challengesnew/css/newquestion.css index 444ca8d1..e0ac5def 100644 --- a/src/modules/tpm/challengesnew/css/newquestion.css +++ b/src/modules/tpm/challengesnew/css/newquestion.css @@ -4,4 +4,13 @@ .Permanentban{ color:#5091FF !important; border-color: #5091FF !important; +} + +/*md编辑器中输入@弹出可选人列表样式*/ +.at_who_list{ + position: absolute; + top: 100px; + left: 100px; + background-color: red; + z-index: 99; } \ No newline at end of file diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index c074d3dc..dfe4e7b1 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -6,6 +6,7 @@ import ResizeObserver from 'resize-observer-polyfill'; import '../../courses/css/Courses.css'; import './css/TPMchallengesnew.css'; import 'codemirror/lib/codemirror.css'; +import './css/newquestion.css'; import { AutoComplete } from 'antd'; const $ = window.$ @@ -75,12 +76,20 @@ function md_elocalStorage(editor, mdu, id) { return tid } +const atWhoList = ( +
      aaa
      +) + +const users = [{image_url: "system/lets/letter_avatars/2/J/241_157_191/120.png",login: "jasonjun",profile_completed: false,user_id: 84965,username: "jasonjun"}, +{image_url: "system/lets/letter_avatars/2/E/122_185_146/120.png",login: "Eo9ygbqns",profile_completed: false,user_id: 84963,username: "Eo9ygbqns"}, +{image_url: "system/lets/letter_avatars/2/P/238_117_19/120.png",login: "postwoman",profile_completed: true,user_id: 84961,username: "PostWoman"}] export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true }) => { const editorEl = useRef(); const resizeBarEl = useRef(); const [editorInstance, setEditorInstance] = useState(); + const [atWhoVisible, setAtWhoVisible] = useState(false); const containerId = `mdEditor_${mdID}`; const editorBodyId = `mdEditors_${mdID}`; const tipId = `e_tips_mdEditor_${mdID}`; @@ -201,18 +210,16 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla } if (onChange) { editorInstance.cm.on('change', (cm) => { - $(`#editorBodyId`).atwho({ - at: '@', - data:['111','222'] - }) - // if(forMember){ - // document.onkeydown = (e) => { - // if (e.key === "@") { - // // 输入@键后在对应的位置显示可选的项目成员 - - // } - // }; - // } + if(forMember){ + document.onkeydown = (e) => { + if (e.key.charCodeAt() === 80 || e.key.charCodeAt() === 64) { + // 输入@键后在对应的位置显示可选的项目成员 + setAtWhoVisible(true); + }else{ + setAtWhoVisible(false); + } + }; + } onChange(cm.getValue()) }) } @@ -230,10 +237,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla useEffect(() => { if (editorInstance && initValue !== undefined) { - $(`#editorBodyId`).atwho({ - at: '@', - data:['111','222'] - }) + const val = editorInstance.getValue(); if (initValue !== null && initValue !== editorInstance.getValue()) { editorInstance.setValue(initValue.toString()) @@ -283,7 +287,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
      - + + {atWhoVisible && atWhoList}
      From 7e69c64c9c65783256e2d3591620e47371629a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Wed, 13 Oct 2021 11:52:54 +0800 Subject: [PATCH 04/28] =?UTF-8?q?@who=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tpm/challengesnew/css/newquestion.css | 22 ++++++++++-- .../tpm/challengesnew/tpm-md-editor.js | 35 ++++++++++++------- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/modules/tpm/challengesnew/css/newquestion.css b/src/modules/tpm/challengesnew/css/newquestion.css index e0ac5def..dbf45193 100644 --- a/src/modules/tpm/challengesnew/css/newquestion.css +++ b/src/modules/tpm/challengesnew/css/newquestion.css @@ -9,8 +9,26 @@ /*md编辑器中输入@弹出可选人列表样式*/ .at_who_list{ position: absolute; + z-index: 99; + width: 180px; + max-height: 160px; + background: #FFFFFF; + box-shadow: 0px 4px 8px 2px rgba(212, 212, 212, 0.5); + border-radius: 4px; top: 100px; left: 100px; - background-color: red; - z-index: 99; + overflow-y: scroll; +} +.at_who{ + height: 40px; + display: flex; + flex-direction: row; + align-items: center; + border-bottom: 1px solid rgba(212, 212, 212, 0.5); +} +.at_who img{ + width:30px; + height:30px; + border-radius:50%; + margin-right: 10px; } \ No newline at end of file diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index dfe4e7b1..4736287c 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -1,13 +1,11 @@ - import React, { Fragment, useEffect, useRef, useState } from 'react'; import { getUploadActionUrl, getUrl } from 'educoder'; import ResizeObserver from 'resize-observer-polyfill'; - +import { getImageUrl } from 'educoder'; import '../../courses/css/Courses.css'; import './css/TPMchallengesnew.css'; import 'codemirror/lib/codemirror.css'; import './css/newquestion.css'; -import { AutoComplete } from 'antd'; const $ = window.$ const mdIcons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "inline-latex", "latex", '|', "image", "table", '|', "line-break", "watch", "clear"]; @@ -41,7 +39,6 @@ function md_rec_data(k, mdu, id) { } window.md_rec_data = md_rec_data; - function md_elocalStorage(editor, mdu, id) { let oc = window.sessionStorage.getItem('content' + mdu) if (oc !== null && oc !== editor.getValue()) { @@ -76,14 +73,6 @@ function md_elocalStorage(editor, mdu, id) { return tid } -const atWhoList = ( -
      aaa
      -) - -const users = [{image_url: "system/lets/letter_avatars/2/J/241_157_191/120.png",login: "jasonjun",profile_completed: false,user_id: 84965,username: "jasonjun"}, -{image_url: "system/lets/letter_avatars/2/E/122_185_146/120.png",login: "Eo9ygbqns",profile_completed: false,user_id: 84963,username: "Eo9ygbqns"}, -{image_url: "system/lets/letter_avatars/2/P/238_117_19/120.png",login: "postwoman",profile_completed: true,user_id: 84961,username: "PostWoman"}] - export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true }) => { const editorEl = useRef(); @@ -93,6 +82,12 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const containerId = `mdEditor_${mdID}`; const editorBodyId = `mdEditors_${mdID}`; const tipId = `e_tips_mdEditor_${mdID}`; + + const users = [{image_url: "system/lets/letter_avatars/2/J/241_157_191/120.png",login: "jasonjun",profile_completed: false,user_id: 84965,username: "jasonjun"}, + {image_url: "system/lets/letter_avatars/2/E/122_185_146/120.png",login: "Eo9ygbqns",profile_completed: false,user_id: 84963,username: "Eo9ygbqns"}, + {image_url: "system/lets/letter_avatars/2/P/238_117_19/120.png",login: "postwoman",profile_completed: true,user_id: 84961,username: "PostWoman"}, + {image_url: "system/lets/letter_avatars/2/X/70_163_90/120.png",login: "Es5ghtfik",profile_completed: false,user_id: 89516,username: "徐准"}, + {image_url: "system/lets/letter_avatars/2/X/70_163_90/120.png",login: "Es5ghtfik",profile_completed: false,user_id: 89516,username: "徐准1"}] function onLayout() { let ro; @@ -111,6 +106,19 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla return ro; } + const atWhoList = ( +
      + {users && users.map((item,key)=>{ + return( +
      + { item.image_url && } + {item.username} +
      + ) + })} +
      + ) + useEffect(() => { if (editorInstance) { return @@ -288,7 +296,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
      - {atWhoVisible && atWhoList} + {/* {atWhoVisible && atWhoList} */} + {atWhoList}
      From b268b540d85ab917ef4b8b778867dea08fada16c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Wed, 13 Oct 2021 14:53:13 +0800 Subject: [PATCH 05/28] =?UTF-8?q?@=E6=88=91=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tpm/challengesnew/css/newquestion.css | 5 +++++ src/modules/tpm/challengesnew/tpm-md-editor.js | 18 +++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/modules/tpm/challengesnew/css/newquestion.css b/src/modules/tpm/challengesnew/css/newquestion.css index dbf45193..ca1ebb05 100644 --- a/src/modules/tpm/challengesnew/css/newquestion.css +++ b/src/modules/tpm/challengesnew/css/newquestion.css @@ -18,6 +18,7 @@ top: 100px; left: 100px; overflow-y: scroll; + cursor: pointer; } .at_who{ height: 40px; @@ -25,6 +26,10 @@ flex-direction: row; align-items: center; border-bottom: 1px solid rgba(212, 212, 212, 0.5); + padding: 0 4px; +} +.at_who:hover{ + background: #F3F4F6; } .at_who img{ width:30px; diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index 4736287c..a8243879 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -106,11 +106,20 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla return ro; } + useEffect(()=>{ + document.addEventListener('click',()=>{setAtWhoVisible(false)}); + }) + + function selectAtWho(username){ + setAtWhoVisible(false); + editorInstance.setValue(initValue.toString()+username); + } + const atWhoList = (
      {users && users.map((item,key)=>{ return( -
      +
      {selectAtWho(item.username)}}> { item.image_url && } {item.username}
      @@ -220,7 +229,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla editorInstance.cm.on('change', (cm) => { if(forMember){ document.onkeydown = (e) => { - if (e.key.charCodeAt() === 80 || e.key.charCodeAt() === 64) { + if (e.key === "@") { // 输入@键后在对应的位置显示可选的项目成员 setAtWhoVisible(true); }else{ @@ -245,7 +254,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla useEffect(() => { if (editorInstance && initValue !== undefined) { - const val = editorInstance.getValue(); if (initValue !== null && initValue !== editorInstance.getValue()) { editorInstance.setValue(initValue.toString()) @@ -296,8 +304,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
      - {/* {atWhoVisible && atWhoList} */} - {atWhoList} + {atWhoVisible && atWhoList} + {/* {atWhoList} */}
      From bc6ef583fb1a04a55d4f683ea59262d668a4c62d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Wed, 13 Oct 2021 16:01:38 +0800 Subject: [PATCH 06/28] atwho --- src/modules/tpm/challengesnew/tpm-md-editor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index a8243879..9d174a65 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -231,6 +231,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla document.onkeydown = (e) => { if (e.key === "@") { // 输入@键后在对应的位置显示可选的项目成员 + const selection = window.getSelection().anchorOffset; setAtWhoVisible(true); }else{ setAtWhoVisible(false); From 7fe88aa4d9926a00e450ee61b25919a40cc2c927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Thu, 14 Oct 2021 09:11:37 +0800 Subject: [PATCH 07/28] =?UTF-8?q?at=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/tpm/challengesnew/tpm-md-editor.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index 9d174a65..f46b478f 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -79,6 +79,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const resizeBarEl = useRef(); const [editorInstance, setEditorInstance] = useState(); const [atWhoVisible, setAtWhoVisible] = useState(false); + const [atWho, setAtWho] = useState(undefined); const containerId = `mdEditor_${mdID}`; const editorBodyId = `mdEditors_${mdID}`; const tipId = `e_tips_mdEditor_${mdID}`; @@ -112,7 +113,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla function selectAtWho(username){ setAtWhoVisible(false); - editorInstance.setValue(initValue.toString()+username); + setAtWho(username); + // console.log('editorInstance1',editorInstance); + // editorInstance.setValue(initValue.toString()+username); + // console.log('editorInstance2',editorInstance); } const atWhoList = ( @@ -231,14 +235,19 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla document.onkeydown = (e) => { if (e.key === "@") { // 输入@键后在对应的位置显示可选的项目成员 - const selection = window.getSelection().anchorOffset; + const endOffset = window.getSelection(); setAtWhoVisible(true); + const addValue = atWho? cm.getValue()+atWho:cm.getValue(); + onChange(addValue); }else{ setAtWhoVisible(false); } }; + }else{ + onChange(cm.getValue()); } - onChange(cm.getValue()) + // const addValue = atWho? cm.getValue():cm.getValue()+atWho; + }) } ro = onLayout() From 4a6012bfa97110bc47c46da98d522da845b7b06b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Thu, 14 Oct 2021 15:04:17 +0800 Subject: [PATCH 08/28] =?UTF-8?q?at=E8=B0=81=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tpm/challengesnew/tpm-md-editor.js | 65 +++++++++++-------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index f46b478f..c1feec48 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -79,7 +79,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const resizeBarEl = useRef(); const [editorInstance, setEditorInstance] = useState(); const [atWhoVisible, setAtWhoVisible] = useState(false); - const [atWho, setAtWho] = useState(undefined); + // const [atWho, setAtWho] = useState(undefined); const containerId = `mdEditor_${mdID}`; const editorBodyId = `mdEditors_${mdID}`; const tipId = `e_tips_mdEditor_${mdID}`; @@ -107,15 +107,18 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla return ro; } - useEffect(()=>{ - document.addEventListener('click',()=>{setAtWhoVisible(false)}); - }) - function selectAtWho(username){ setAtWhoVisible(false); - setAtWho(username); + const a = editorInstance.cm; + const b = editorInstance.cm.on; + console.log('cm.getValue()', editorInstance.cm) + console.log('onchange', editorInstance.cm.doc.getCursor()); + editorInstance.setValue(initValue.toString()+username); + // editorInstance.cm.on('change',(cm)=>{ + // onchange(cm.getValue()+username); + // }) + // setAtWho(username); // console.log('editorInstance1',editorInstance); - // editorInstance.setValue(initValue.toString()+username); // console.log('editorInstance2',editorInstance); } @@ -131,6 +134,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla })}
      ) + + useEffect(()=>{ + document.addEventListener('click',()=>{setAtWhoVisible(false)}); + }) useEffect(() => { if (editorInstance) { @@ -229,26 +236,32 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla if (!noStorage) { tid = md_elocalStorage(editorInstance, `MDEditor__${containerId}`, containerId) } + editorInstance.cm.on('change', (cm) => { + if(forMember){ + document.onkeydown = (e) => { + console.log('onkeydown', e) + if (e.key === "@") { + // 输入@键后在对应的位置显示可选的项目成员 + // const endOffset = window.getSelection(); + setAtWhoVisible(true); + // const addValue = atWho? cm.getValue()+atWho:cm.getValue(); + // onChange(addValue); + }else{ + setAtWhoVisible(false); + } + }; + } + console.log('change---') + onChange && onChange(cm.getValue()); + // atWho && onChange(addValue+atWho) && setAtWho(undefined); + // if(atWho){ + // onChange(cm.getValue()+atWho); + // setAtWho(undefined); + // }else{ + // onChange(cm.getValue()); + // } + }) if (onChange) { - editorInstance.cm.on('change', (cm) => { - if(forMember){ - document.onkeydown = (e) => { - if (e.key === "@") { - // 输入@键后在对应的位置显示可选的项目成员 - const endOffset = window.getSelection(); - setAtWhoVisible(true); - const addValue = atWho? cm.getValue()+atWho:cm.getValue(); - onChange(addValue); - }else{ - setAtWhoVisible(false); - } - }; - }else{ - onChange(cm.getValue()); - } - // const addValue = atWho? cm.getValue():cm.getValue()+atWho; - - }) } ro = onLayout() return () => { From 4add2660476c95b56d46475c446b2b14fdf8d5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Fri, 15 Oct 2021 11:36:51 +0800 Subject: [PATCH 09/28] =?UTF-8?q?at=E8=B0=81=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tpm/challengesnew/css/newquestion.css | 2 +- .../tpm/challengesnew/tpm-md-editor.js | 44 +++++++------------ 2 files changed, 17 insertions(+), 29 deletions(-) diff --git a/src/modules/tpm/challengesnew/css/newquestion.css b/src/modules/tpm/challengesnew/css/newquestion.css index ca1ebb05..46bb22c3 100644 --- a/src/modules/tpm/challengesnew/css/newquestion.css +++ b/src/modules/tpm/challengesnew/css/newquestion.css @@ -15,7 +15,7 @@ background: #FFFFFF; box-shadow: 0px 4px 8px 2px rgba(212, 212, 212, 0.5); border-radius: 4px; - top: 100px; + top: 60px; left: 100px; overflow-y: scroll; cursor: pointer; diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index c1feec48..eae4f23e 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -79,7 +79,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const resizeBarEl = useRef(); const [editorInstance, setEditorInstance] = useState(); const [atWhoVisible, setAtWhoVisible] = useState(false); - // const [atWho, setAtWho] = useState(undefined); const containerId = `mdEditor_${mdID}`; const editorBodyId = `mdEditors_${mdID}`; const tipId = `e_tips_mdEditor_${mdID}`; @@ -109,19 +108,17 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla function selectAtWho(username){ setAtWhoVisible(false); - const a = editorInstance.cm; - const b = editorInstance.cm.on; - console.log('cm.getValue()', editorInstance.cm) - console.log('onchange', editorInstance.cm.doc.getCursor()); - editorInstance.setValue(initValue.toString()+username); - // editorInstance.cm.on('change',(cm)=>{ - // onchange(cm.getValue()+username); - // }) - // setAtWho(username); - // console.log('editorInstance1',editorInstance); - // console.log('editorInstance2',editorInstance); + const cm = editorInstance.cm; + //获取鼠标所在行的行数和ch + const cursor = cm.doc.getCursor(); + const line = cursor.line;//行 + const ch = cursor.ch;//列 + //替换最后的内容 + cm.replaceRange(username+" ",{line,ch},{line,ch}); } + console.log('window.event',window.event); + const atWhoList = (
      {users && users.map((item,key)=>{ @@ -236,31 +233,22 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla if (!noStorage) { tid = md_elocalStorage(editorInstance, `MDEditor__${containerId}`, containerId) } - editorInstance.cm.on('change', (cm) => { - if(forMember){ + //当光标或选中内容时触发绑定@事件 + editorInstance.cm.on("cursorActivity", () => { + if (forMember) { document.onkeydown = (e) => { - console.log('onkeydown', e) if (e.key === "@") { // 输入@键后在对应的位置显示可选的项目成员 - // const endOffset = window.getSelection(); setAtWhoVisible(true); - // const addValue = atWho? cm.getValue()+atWho:cm.getValue(); - // onChange(addValue); - }else{ + } else { setAtWhoVisible(false); } }; } - console.log('change---') + }); + editorInstance.cm.on("change", (cm) => { onChange && onChange(cm.getValue()); - // atWho && onChange(addValue+atWho) && setAtWho(undefined); - // if(atWho){ - // onChange(cm.getValue()+atWho); - // setAtWho(undefined); - // }else{ - // onChange(cm.getValue()); - // } - }) + }); if (onChange) { } ro = onLayout() From e3f49db5c0d6a03147453e9d081947cb6826234b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Mon, 18 Oct 2021 14:19:32 +0800 Subject: [PATCH 10/28] =?UTF-8?q?at=E8=B0=81=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tpm/challengesnew/css/newquestion.css | 2 - .../tpm/challengesnew/tpm-md-editor.js | 65 ++++++++++++++++--- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/modules/tpm/challengesnew/css/newquestion.css b/src/modules/tpm/challengesnew/css/newquestion.css index 46bb22c3..13353547 100644 --- a/src/modules/tpm/challengesnew/css/newquestion.css +++ b/src/modules/tpm/challengesnew/css/newquestion.css @@ -15,8 +15,6 @@ background: #FFFFFF; box-shadow: 0px 4px 8px 2px rgba(212, 212, 212, 0.5); border-radius: 4px; - top: 60px; - left: 100px; overflow-y: scroll; cursor: pointer; } diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index eae4f23e..f3455fe4 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -117,10 +117,51 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla cm.replaceRange(username+" ",{line,ch},{line,ch}); } - console.log('window.event',window.event); - + // useEffect (()=>{ + // //监听上下键和enter键 选中atwo + // atWhoVisible && document.onkeydown((e)=>{ + // //上 + // if(e.key === "ArrowUp"){ + // console.log('上'); + // } + // //下 + // if(e.key === "ArrowDown"){ + // console.log('下'); + // } + // //enter + // if(e.key === "Enter"){ + // console.log('Enter'); + // } + // }) + // },[]) + + // function handleKeyDown(e){ + // //上 + // if(e.key === "ArrowUp"){ + // console.log('上'); + // } + // //下 + // if(e.key === "ArrowDown"){ + // console.log('下'); + // } + // //enter + // if(e.key === "Enter"){ + // console.log('Enter'); + // } + // } + const atWhoList = ( -
      + // + // {users && users.map((item,key)=>{ + // return( + // + // { item.image_url && } + // {item.username} + // + // ) + // })} + // +
      {handleKeyDown(e)}}> {users && users.map((item,key)=>{ return(
      {selectAtWho(item.username)}}> @@ -131,7 +172,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla })}
      ) - + useEffect(()=>{ document.addEventListener('click',()=>{setAtWhoVisible(false)}); }) @@ -234,18 +275,27 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla tid = md_elocalStorage(editorInstance, `MDEditor__${containerId}`, containerId) } //当光标或选中内容时触发绑定@事件 - editorInstance.cm.on("cursorActivity", () => { + editorInstance.cm.on("focus", () => { if (forMember) { document.onkeydown = (e) => { + // console.log('e',e); if (e.key === "@") { // 输入@键后在对应的位置显示可选的项目成员 setAtWhoVisible(true); + //获取光标位置 + const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style; + //设置弹框位置 + document.getElementById("at_who_list").style.top = (parseInt(cssStyle.getPropertyValue("top").replace("px",""))+60)+"px"; + document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+20)+"px"; } else { setAtWhoVisible(false); } }; } }); + editorInstance.cm.on("blur", () => { + document.onkeydown = null ; + }); editorInstance.cm.on("change", (cm) => { onChange && onChange(cm.getValue()); }); @@ -309,15 +359,14 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla }, [ editorInstance, resizeBarEl ]) - return ( -
      +
      +
      {atWhoVisible && atWhoList} {/* {atWhoList} */} -
      {showResizeBar ? : null} From 18b289b84c36d4a4d64ef538844cc6744cb7e9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Thu, 21 Oct 2021 09:41:17 +0800 Subject: [PATCH 11/28] =?UTF-8?q?@who=EF=BC=88=E7=BC=BA=E4=BC=98=E5=8C=96?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forge/Merge/merge_form.js | 20 ++- src/forge/Order/order_form.js | 24 ++- src/forge/comments/comments.js | 24 ++- .../tpm/challengesnew/tpm-md-editor.js | 150 ++++++++++-------- 4 files changed, 151 insertions(+), 67 deletions(-) diff --git a/src/forge/Merge/merge_form.js b/src/forge/Merge/merge_form.js index 5df9794b..7bdfc04e 100644 --- a/src/forge/Merge/merge_form.js +++ b/src/forge/Merge/merge_form.js @@ -25,6 +25,7 @@ class MergeForm extends Component { issue_tags: undefined, issue_versions: undefined, issue_priories: undefined, + atWhoLoginList:undefined }; } @@ -146,7 +147,15 @@ class MergeForm extends Component { } else { values.issue_tag_ids = []; } - const { desc } = this.state; + const { desc , atWhoLoginList } = this.state; + //发送消息 + if(atWhoLoginList.length != 0){ + axios.post(`/users/${owner}/messages.json`,{ + type:"atme", + receivers_login:atWhoLoginList, + atmeable_type:"PullRequest" + }) + } if (merge_type === "new") { let url = `/${owner}/${projectsId}/pulls.json`; axios.post(url, { @@ -224,6 +233,13 @@ class MergeForm extends Component { }); }; + //合并请求中at谁列表(存储:login) + changeAtWhoLoginList = (loginList) =>{ + this.setState({ + atWhoLoginList:loginList, + }); + }; + render() { const { merge_type } = this.props; const { getFieldDecorator } = this.props.form; @@ -272,6 +288,8 @@ class MergeForm extends Component { mdID={"merge-new-description"} initValue={desc} onChange={this.onContentChange} + isCanAtme = {true} + changeAtWhoLoginList = {this.changeAtWhoLoginList} >

      {get_attachments && get_attachments.length > 0 ? ( diff --git a/src/forge/comments/comments.js b/src/forge/comments/comments.js index c27bb12a..aeb59224 100644 --- a/src/forge/comments/comments.js +++ b/src/forge/comments/comments.js @@ -30,6 +30,7 @@ class comments extends Component { reply_id: undefined, reply_content: undefined, new_journal_id: undefined, + atWhoLoginList:undefined }; } @@ -49,6 +50,7 @@ class comments extends Component { }); return; } + this.props.form.validateFieldsAndScroll((err, values) => { if (!err) { const { @@ -58,10 +60,21 @@ class comments extends Component { orderId, reply_id, is_reply, + atWhoLoginList, } = this.state; - const url = `/issues/${orderId}/journals.json`; + const { owner } = this.props.match.params; + //发送消息 + if(atWhoLoginList.length != 0){ + axios.post(`/users/${owner}/messages.json`,{ + type:"atme", + receivers_login:atWhoLoginList, + atmeable_type:"Issue" + }) + } + + const url = `/issues/${orderId}/journals.json`; axios .post(url, { ...values, @@ -258,6 +271,13 @@ class comments extends Component { } }; + //评论中at谁列表(存储:login) + changeAtWhoLoginList = (loginList) =>{ + this.setState({ + atWhoLoginList:loginList, + }); + }; + onRef = (ref) => { this.child = ref; }; @@ -335,6 +355,8 @@ class comments extends Component { onChange={ is_reply ? this.replyContentChange : this.onContentChange } + isCanAtme = {true} + changeAtWhoLoginList = {this.changeAtWhoLoginList} >

      {quillFlag && 请输入评论内容} diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index f3455fe4..f6c4d66d 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -73,12 +73,14 @@ function md_elocalStorage(editor, mdu, id) { return tid } -export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true }) => { +export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true , isCanAtme = false ,changeAtWhoLoginList }) => { const editorEl = useRef(); const resizeBarEl = useRef(); const [editorInstance, setEditorInstance] = useState(); const [atWhoVisible, setAtWhoVisible] = useState(false); + const [atWhoLoginListState, setAtWhoLoginListState] = useState([]); + const atWhoLoginList = useRef([]); const containerId = `mdEditor_${mdID}`; const editorBodyId = `mdEditors_${mdID}`; const tipId = `e_tips_mdEditor_${mdID}`; @@ -86,7 +88,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const users = [{image_url: "system/lets/letter_avatars/2/J/241_157_191/120.png",login: "jasonjun",profile_completed: false,user_id: 84965,username: "jasonjun"}, {image_url: "system/lets/letter_avatars/2/E/122_185_146/120.png",login: "Eo9ygbqns",profile_completed: false,user_id: 84963,username: "Eo9ygbqns"}, {image_url: "system/lets/letter_avatars/2/P/238_117_19/120.png",login: "postwoman",profile_completed: true,user_id: 84961,username: "PostWoman"}, - {image_url: "system/lets/letter_avatars/2/X/70_163_90/120.png",login: "Es5ghtfik",profile_completed: false,user_id: 89516,username: "徐准"}, + {image_url: "system/lets/letter_avatars/2/X/70_163_90/120.png",login: "xuzhun",profile_completed: false,user_id: 89516,username: "徐准"}, {image_url: "system/lets/letter_avatars/2/X/70_163_90/120.png",login: "Es5ghtfik",profile_completed: false,user_id: 89516,username: "徐准1"}] function onLayout() { @@ -115,53 +117,23 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const ch = cursor.ch;//列 //替换最后的内容 cm.replaceRange(username+" ",{line,ch},{line,ch}); + //将此user的login存储到atWhoLoginList集合中 + atWhoLoginListState + const list = new Set(atWhoLoginList.current); + users.map((item)=>{ + item.username === username && list.add(item.login); + }) + atWhoLoginList.current = Array.from(list); + setAtWhoLoginListState(Array.from(list)); } - // useEffect (()=>{ - // //监听上下键和enter键 选中atwo - // atWhoVisible && document.onkeydown((e)=>{ - // //上 - // if(e.key === "ArrowUp"){ - // console.log('上'); - // } - // //下 - // if(e.key === "ArrowDown"){ - // console.log('下'); - // } - // //enter - // if(e.key === "Enter"){ - // console.log('Enter'); - // } - // }) - // },[]) - - // function handleKeyDown(e){ - // //上 - // if(e.key === "ArrowUp"){ - // console.log('上'); - // } - // //下 - // if(e.key === "ArrowDown"){ - // console.log('下'); - // } - // //enter - // if(e.key === "Enter"){ - // console.log('Enter'); - // } - // } + useEffect(()=>{ + console.log('@谁列表发生变化,atWhoLoginList.current:',atWhoLoginList.current,'atWhoLoginListState: ',atWhoLoginListState); + changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState); + },[atWhoLoginListState]) const atWhoList = ( - //

      - // {users && users.map((item,key)=>{ - // return( - // - // { item.image_url && } - // {item.username} - // - // ) - // })} - // -
      {handleKeyDown(e)}}> +
      {users && users.map((item,key)=>{ return(
      {selectAtWho(item.username)}}> @@ -274,24 +246,78 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla if (!noStorage) { tid = md_elocalStorage(editorInstance, `MDEditor__${containerId}`, containerId) } + //isCanAtme:只有issue和合并请求以及评论部分可以@他人操作 //当光标或选中内容时触发绑定@事件 - editorInstance.cm.on("focus", () => { - if (forMember) { - document.onkeydown = (e) => { - // console.log('e',e); - if (e.key === "@") { - // 输入@键后在对应的位置显示可选的项目成员 - setAtWhoVisible(true); - //获取光标位置 - const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style; - //设置弹框位置 - document.getElementById("at_who_list").style.top = (parseInt(cssStyle.getPropertyValue("top").replace("px",""))+60)+"px"; - document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+20)+"px"; - } else { - setAtWhoVisible(false); + isCanAtme && editorInstance.cm.on("focus", () => { + document.onkeydown = (e) => { + if (e.key === "@") { + // 输入@键后在对应的位置显示可选的项目成员 + setAtWhoVisible(true); + //获取光标位置 + const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style; + //设置弹框位置 + document.getElementById("at_who_list").style.top = (parseInt(cssStyle.getPropertyValue("top").replace("px",""))+60)+"px"; + document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+20)+"px"; + } else { + setAtWhoVisible(false); + } + if(atWhoLoginList.current.length != 0){ + console.log('initValue',initValue); + const codemirror = editorInstance.cm; + const value = codemirror.getValue(); + //处理初始内容就自带@谁的情况 + if(initValue){ + users.map(item=>{ + if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ + //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 + value.replace(`@${username}`,""); + } + }) + Array.from(atWhoMap.keys()).map(username=>{ + if(initValue.indexOf(username)!=-1 && value.charAt(value.indexOf(username)-1) === "@"){ + + } + }) + console.log('剔除初始内容中的@who',value); } - }; - } + //以username为主键,login为value的map集合 + let atWhoMap = new Map(); + Array.from(atWhoLoginList.current).map(item=>{ + users.map(i=>{ + if(i.login === item){ + atWhoMap.set(i.username,i.login); + } + }) + }); + if(value.indexOf("@") === -1){ + //已经有要@的列表,但是没有@符号 -> 清空@集合 + atWhoLoginList.current = []; + setAtWhoLoginListState([]); + return; + } + const cursor = codemirror.doc.getCursor(); + const line = cursor.line; + const ch = cursor.ch; + const lineContent = codemirror.getLine(line); + if(lineContent && lineContent.indexOf("@") != -1){//此行有@谁 + Array.from(atWhoMap.keys()).map(username=>{ + //判断lineContent是不是以列表中的某个username结尾 + if(lineContent.endsWith(username)){ + codemirror.setSelection({line,ch:ch-username.length-1},{line,ch}); + return; + } + //处理有名字但是无@符号,有@但是名字对不上的情况 + if(value.indexOf(username)===-1 || value.charAt(value.indexOf(username)-1) !="@"){ + //符合任意一种情况->踢掉这个人 不给他发消息 + const list = new Set(atWhoLoginList.current); + list.delete(atWhoMap.get(username)); + atWhoLoginList.current = Array.from(list); + setAtWhoLoginListState(Array.from(list)); + } + }) + } + } + }; }); editorInstance.cm.on("blur", () => { document.onkeydown = null ; @@ -299,8 +325,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla editorInstance.cm.on("change", (cm) => { onChange && onChange(cm.getValue()); }); - if (onChange) { - } ro = onLayout() return () => { if (!noStorage) { From 4f3e385f783df39a0a0e7fa51f5a43944db13dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Thu, 21 Oct 2021 10:51:40 +0800 Subject: [PATCH 12/28] =?UTF-8?q?at=E8=B0=81=E5=88=97=E8=A1=A8=EF=BC=88?= =?UTF-8?q?=E7=BC=BA=E5=BC=B9=E6=A1=86=E7=9A=84=E4=B8=8A=E4=B8=8B=EF=BC=8C?= =?UTF-8?q?enter=E9=94=AE=E7=9B=91=E5=90=AC=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tpm/challengesnew/tpm-md-editor.js | 109 +++++++++++------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index f6c4d66d..5579f33b 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -118,7 +118,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //替换最后的内容 cm.replaceRange(username+" ",{line,ch},{line,ch}); //将此user的login存储到atWhoLoginList集合中 - atWhoLoginListState const list = new Set(atWhoLoginList.current); users.map((item)=>{ item.username === username && list.add(item.login); @@ -261,56 +260,30 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla } else { setAtWhoVisible(false); } - if(atWhoLoginList.current.length != 0){ - console.log('initValue',initValue); + //处理本来@了某人 -> 删掉 -> 撤回 的情况 + if(e.code === "KeyZ" && users.length != 0){ const codemirror = editorInstance.cm; - const value = codemirror.getValue(); + let value = codemirror.getValue(); //处理初始内容就自带@谁的情况 if(initValue){ + const del = []; users.map(item=>{ if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 - value.replace(`@${username}`,""); + del[del.length] = `@${item.username}`; } }) - Array.from(atWhoMap.keys()).map(username=>{ - if(initValue.indexOf(username)!=-1 && value.charAt(value.indexOf(username)-1) === "@"){ - - } + del.length!=0 && del.map(str=>{ + value = value.replace(str,""); }) - console.log('剔除初始内容中的@who',value); } - //以username为主键,login为value的map集合 - let atWhoMap = new Map(); - Array.from(atWhoLoginList.current).map(item=>{ - users.map(i=>{ - if(i.login === item){ - atWhoMap.set(i.username,i.login); - } - }) - }); - if(value.indexOf("@") === -1){ - //已经有要@的列表,但是没有@符号 -> 清空@集合 - atWhoLoginList.current = []; - setAtWhoLoginListState([]); - return; - } - const cursor = codemirror.doc.getCursor(); - const line = cursor.line; - const ch = cursor.ch; - const lineContent = codemirror.getLine(line); - if(lineContent && lineContent.indexOf("@") != -1){//此行有@谁 - Array.from(atWhoMap.keys()).map(username=>{ - //判断lineContent是不是以列表中的某个username结尾 - if(lineContent.endsWith(username)){ - codemirror.setSelection({line,ch:ch-username.length-1},{line,ch}); - return; - } - //处理有名字但是无@符号,有@但是名字对不上的情况 - if(value.indexOf(username)===-1 || value.charAt(value.indexOf(username)-1) !="@"){ - //符合任意一种情况->踢掉这个人 不给他发消息 + //判断value是否包含@符号 + if(value.indexOf("@") != -1){ + users.map(item =>{ + if(value.indexOf(item.username)!=-1 && value.charAt(value.indexOf(item.username)-1) ==="@"){ + //将此user的login存储到atWhoLoginList集合中 const list = new Set(atWhoLoginList.current); - list.delete(atWhoMap.get(username)); + list.add(item.login); atWhoLoginList.current = Array.from(list); setAtWhoLoginListState(Array.from(list)); } @@ -324,6 +297,62 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla }); editorInstance.cm.on("change", (cm) => { onChange && onChange(cm.getValue()); + if(atWhoLoginList.current.length != 0){ + const codemirror = editorInstance.cm; + let value = codemirror.getValue(); + //处理初始内容就自带@谁的情况 + if(initValue){ + const del = []; + users.map(item=>{ + if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ + //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 + del[del.length] = `@${item.username}`; + } + }) + del.length!=0 && del.map(str=>{ + value = value.replace(str,""); + }) + } + //以username为主键,login为value的map集合 + let atWhoMap = new Map(); + Array.from(atWhoLoginList.current).map(item=>{ + users.map(i=>{ + if(i.login === item){ + atWhoMap.set(i.username,i.login); + } + }) + }); + if(value.indexOf("@") === -1){ + //已经有要@的列表,但是没有@符号 -> 清空@集合 + atWhoLoginList.current = []; + setAtWhoLoginListState([]); + return; + } + const cursor = codemirror.doc.getCursor(); + const line = cursor.line; + const ch = cursor.ch; + const lineContent = codemirror.getLine(line); + if(lineContent && lineContent.indexOf("@") != -1){//此行有@谁 + Array.from(atWhoMap.keys()).map(username=>{ + //判断lineContent是不是以列表中的某个username结尾 + if(lineContent.endsWith(username)){ + codemirror.setSelection({line,ch:ch-username.length-1},{line,ch}); + return; + } + //处理有名字但是无@符号,有@但是名字对不上的情况 + const a = value.indexOf(username)===-1; + const b = value.charAt(value.indexOf(username)-1) !="@"; + if(value.indexOf(username)===-1 || value.charAt(value.indexOf(username)-1) !="@"){ + //符合任意一种情况->踢掉这个人 不给他发消息 + const list = new Set(atWhoLoginList.current); + list.delete(atWhoMap.get(username)); + atWhoLoginList.current = Array.from(list); + setAtWhoLoginListState(Array.from(list)); + } + }) + } + } + }); ro = onLayout() return () => { From 3498390974be49da9d3e3e3dbc0daa624d4984e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Thu, 21 Oct 2021 14:12:15 +0800 Subject: [PATCH 13/28] at --- src/modules/tpm/challengesnew/tpm-md-editor.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index 5579f33b..2aaa879e 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -117,6 +117,9 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const ch = cursor.ch;//列 //替换最后的内容 cm.replaceRange(username+" ",{line,ch},{line,ch}); + //鼠标聚焦到此行的最后 + editorInstance.focus(); + editorInstance.setCursor({line,ch:ch+username.length+1}); //将此user的login存储到atWhoLoginList集合中 const list = new Set(atWhoLoginList.current); users.map((item)=>{ @@ -148,6 +151,15 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla document.addEventListener('click',()=>{setAtWhoVisible(false)}); }) + useEffect(()=>{ + //当atWhoVisible为true的时候,失焦,监听上下和enter键 + if(atWhoVisible && editorInstance && editorInstance.cm){ + console.log('弹框啦'); + //焦点转移到非monaco-editer编辑器上 + editorInstance.cm.addEventListener('blur',()=>{}) + } + },[atWhoVisible]) + useEffect(() => { if (editorInstance) { return @@ -352,7 +364,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla }) } } - }); ro = onLayout() return () => { @@ -368,7 +379,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla useEffect(() => { if (editorInstance && initValue !== undefined) { - const val = editorInstance.getValue(); if (initValue !== null && initValue !== editorInstance.getValue()) { editorInstance.setValue(initValue.toString()) } @@ -419,7 +429,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
      {atWhoVisible && atWhoList} - {/* {atWhoList} */}
      {showResizeBar ? : null} From 8021d96cd8e5f95db2317c779daa8dd0621000d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Sat, 23 Oct 2021 19:39:54 +0800 Subject: [PATCH 14/28] =?UTF-8?q?atwho=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forge/Merge/merge_form.js | 14 +- src/forge/Order/order_form.js | 11 +- src/forge/comments/comments.js | 10 +- .../tpm/challengesnew/css/newquestion.css | 8 +- .../tpm/challengesnew/tpm-md-editor.js | 172 ++++++++++++------ 5 files changed, 125 insertions(+), 90 deletions(-) diff --git a/src/forge/Merge/merge_form.js b/src/forge/Merge/merge_form.js index 7bdfc04e..8ca1125d 100644 --- a/src/forge/Merge/merge_form.js +++ b/src/forge/Merge/merge_form.js @@ -147,15 +147,7 @@ class MergeForm extends Component { } else { values.issue_tag_ids = []; } - const { desc , atWhoLoginList } = this.state; - //发送消息 - if(atWhoLoginList.length != 0){ - axios.post(`/users/${owner}/messages.json`,{ - type:"atme", - receivers_login:atWhoLoginList, - atmeable_type:"PullRequest" - }) - } + const { desc , atWhoLoginList } = this.state; if (merge_type === "new") { let url = `/${owner}/${projectsId}/pulls.json`; axios.post(url, { @@ -167,7 +159,8 @@ class MergeForm extends Component { fork_project_id: data && data.fork_project_id, merge_user_login: data && data.merge_user_login, files_count, - commits_count + commits_count, + receivers_login:atWhoLoginList, }) .then((result) => { if (result) { @@ -197,6 +190,7 @@ class MergeForm extends Component { body: desc, head: pull, base: merge, + receivers_login:atWhoLoginList, }) .then((result) => { if (result) { diff --git a/src/forge/Order/order_form.js b/src/forge/Order/order_form.js index b019c098..2cb96ed7 100644 --- a/src/forge/Order/order_form.js +++ b/src/forge/Order/order_form.js @@ -154,15 +154,6 @@ class order_form extends Component { values.issue_tag_ids = [values.issue_tag_ids]; } const { description, start_date, due_date, issue_type , atWhoLoginList } = this.state; - //发送消息 - if(atWhoLoginList.length != 0){ - console.log('issue发送消息',atWhoLoginList); - axios.post(`/users/${owner}/messages.json`,{ - type:"atme", - receivers_login:atWhoLoginList, - atmeable_type:"Issue" - }) - } if (form_type !== "edit") { const url = `/${owner}/${projectsId}/issues.json`; axios.post(url, { @@ -172,6 +163,7 @@ class order_form extends Component { start_date: start_date, due_date: due_date, issue_type: issue_type, + receivers_login:atWhoLoginList, }).then((result) => { if (result && result.data.id) { this.props.showNotification("任务创建成功!"); @@ -198,6 +190,7 @@ class order_form extends Component { due_date: due_date, issue_type: issue_type, ...values, + receivers_login:atWhoLoginList, }).then((result) => { if (result) { this.props.history.push(`/${owner}/${projectsId}/issues/${orderId}`); diff --git a/src/forge/comments/comments.js b/src/forge/comments/comments.js index aeb59224..84478d7c 100644 --- a/src/forge/comments/comments.js +++ b/src/forge/comments/comments.js @@ -65,15 +65,6 @@ class comments extends Component { const { owner } = this.props.match.params; - //发送消息 - if(atWhoLoginList.length != 0){ - axios.post(`/users/${owner}/messages.json`,{ - type:"atme", - receivers_login:atWhoLoginList, - atmeable_type:"Issue" - }) - } - const url = `/issues/${orderId}/journals.json`; axios .post(url, { @@ -82,6 +73,7 @@ class comments extends Component { issue_id: orderId, attachment_ids: fileList, parent_id: reply_id, + receivers_login:atWhoLoginList, }) .then((result) => { if (result && result.data.status === 0) { diff --git a/src/modules/tpm/challengesnew/css/newquestion.css b/src/modules/tpm/challengesnew/css/newquestion.css index 13353547..c6359f52 100644 --- a/src/modules/tpm/challengesnew/css/newquestion.css +++ b/src/modules/tpm/challengesnew/css/newquestion.css @@ -9,7 +9,7 @@ /*md编辑器中输入@弹出可选人列表样式*/ .at_who_list{ position: absolute; - z-index: 99; + z-index: 100; width: 180px; max-height: 160px; background: #FFFFFF; @@ -26,7 +26,7 @@ border-bottom: 1px solid rgba(212, 212, 212, 0.5); padding: 0 4px; } -.at_who:hover{ +.active{ background: #F3F4F6; } .at_who img{ @@ -34,4 +34,8 @@ height:30px; border-radius:50%; margin-right: 10px; +} +.blur_atWho{ + position: absolute; + top: -100px; } \ No newline at end of file diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index 2aaa879e..437d2bce 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -2,6 +2,7 @@ import React, { Fragment, useEffect, useRef, useState } from 'react'; import { getUploadActionUrl, getUrl } from 'educoder'; import ResizeObserver from 'resize-observer-polyfill'; import { getImageUrl } from 'educoder'; +import axios from 'axios'; import '../../courses/css/Courses.css'; import './css/TPMchallengesnew.css'; import 'codemirror/lib/codemirror.css'; @@ -80,16 +81,23 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const [editorInstance, setEditorInstance] = useState(); const [atWhoVisible, setAtWhoVisible] = useState(false); const [atWhoLoginListState, setAtWhoLoginListState] = useState([]); + const [users, setUsers] = useState([]); const atWhoLoginList = useRef([]); const containerId = `mdEditor_${mdID}`; const editorBodyId = `mdEditors_${mdID}`; const tipId = `e_tips_mdEditor_${mdID}`; - - const users = [{image_url: "system/lets/letter_avatars/2/J/241_157_191/120.png",login: "jasonjun",profile_completed: false,user_id: 84965,username: "jasonjun"}, - {image_url: "system/lets/letter_avatars/2/E/122_185_146/120.png",login: "Eo9ygbqns",profile_completed: false,user_id: 84963,username: "Eo9ygbqns"}, - {image_url: "system/lets/letter_avatars/2/P/238_117_19/120.png",login: "postwoman",profile_completed: true,user_id: 84961,username: "PostWoman"}, - {image_url: "system/lets/letter_avatars/2/X/70_163_90/120.png",login: "xuzhun",profile_completed: false,user_id: 89516,username: "徐准"}, - {image_url: "system/lets/letter_avatars/2/X/70_163_90/120.png",login: "Es5ghtfik",profile_completed: false,user_id: 89516,username: "徐准1"}] + + useEffect(()=>{ + isCanAtme && axios.get('/users/list.json',{ + params: { + search: 'admin', + }, + }).then(response=>{ + if(response && response.status === 200){ + setUsers(response.data.users); + } + }) + },[]) function onLayout() { let ro; @@ -116,10 +124,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const line = cursor.line;//行 const ch = cursor.ch;//列 //替换最后的内容 + // cm.getLine(line).endsWith("@") ? cm.replaceRange(username+" ",{line,ch},{line,ch}) : cm.replaceRange("@"+username+" ",{line,ch},{line,ch}) cm.replaceRange(username+" ",{line,ch},{line,ch}); - //鼠标聚焦到此行的最后 - editorInstance.focus(); - editorInstance.setCursor({line,ch:ch+username.length+1}); + //鼠标聚焦 + cm.focus(); //将此user的login存储到atWhoLoginList集合中 const list = new Set(atWhoLoginList.current); users.map((item)=>{ @@ -129,6 +137,92 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla setAtWhoLoginListState(Array.from(list)); } + function onMouseOver(key){ + document.getElementsByClassName("at_who active")[0].className="at_who"; + document.getElementsByClassName("at_who")[key].className="at_who active"; + } + + //markdown编辑器中输入的键盘监听事件 + function mdKeyDown(){ + document.onkeydown = e=>{ + console.log("markdown",atWhoVisible); + if (e.key === "@") { + // 输入@键后在对应的位置显示可选的项目成员 + setAtWhoVisible(true); + //获取光标位置 + const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style; + //设置弹框位置 + document.getElementById("at_who_list").style.top = (parseInt(cssStyle.getPropertyValue("top").replace("px",""))+60)+"px"; + document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+20)+"px"; + //将第一个用户默认选中 + const at_who_divs = document.getElementsByClassName("at_who"); + at_who_divs[0].className = "at_who active"; + } else { + setAtWhoVisible(false); + } + //处理本来@了某人 -> 删掉 -> 撤回 的情况 + if(e.code === "KeyZ" && users.length != 0){ + const codemirror = editorInstance.cm; + let value = codemirror.getValue(); + //处理初始内容就自带@谁的情况 + if(initValue){ + const del = []; + users.map(item=>{ + if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ + //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 + del[del.length] = `@${item.username}`; + } + }) + del.length!=0 && del.map(str=>{ + value = value.replace(str,""); + }) + } + //判断value是否包含@符号 + value.indexOf("@") != -1 && users.map(item =>{ + if(value.indexOf(item.username)!=-1 && value.charAt(value.indexOf(item.username)-1) ==="@"){ + //将此user的login存储到atWhoLoginList集合中 + const list = new Set(atWhoLoginList.current); + list.add(item.login); + atWhoLoginList.current = Array.from(list); + setAtWhoLoginListState(Array.from(list)); + } + }) + } + } + } + + //弹出可选@用户列表之后的键盘监听事件 + function atWhoKeyDown(){ + //监听上下和enter键 + document.onkeydown = (e) =>{ + console.log("atwho列表",atWhoVisible); + const atWhoListDiv = document.getElementById("at_who_list"); + const atWhoDivs = document.getElementsByClassName("at_who"); + let index; + for(let i = 0; i 0){ + // index >=4 && (atWhoListDiv.scrollTop -=40) + atWhoListDiv.scrollTop -= 40; + atWhoDivs[index].className = "at_who"; + atWhoDivs[index-1].className = "at_who active"; + } + if(e.key === "ArrowDown" && index < atWhoDivs.length-1){ + // index >=3 && (atWhoListDiv.scrollTop +=40) + atWhoListDiv.scrollTop += 40; + atWhoDivs[index].className = "at_who"; + atWhoDivs[index+1].className = "at_who active"; + } + if(e.key === "Enter"){ + //阻止默认事件 + e.preventDefault(); + //找到classname为at_who active的div,执行click事件 + document.getElementsByClassName("at_who active")[0].click(); + } + } + } + useEffect(()=>{ console.log('@谁列表发生变化,atWhoLoginList.current:',atWhoLoginList.current,'atWhoLoginListState: ',atWhoLoginListState); changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState); @@ -138,8 +232,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
      {users && users.map((item,key)=>{ return( -
      {selectAtWho(item.username)}}> - { item.image_url && } +
      {selectAtWho(item.username)}} onMouseOver={()=>{onMouseOver(key)}}> + {item.image_url && } {item.username}
      ) @@ -153,10 +247,9 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla useEffect(()=>{ //当atWhoVisible为true的时候,失焦,监听上下和enter键 - if(atWhoVisible && editorInstance && editorInstance.cm){ - console.log('弹框啦'); - //焦点转移到非monaco-editer编辑器上 - editorInstance.cm.addEventListener('blur',()=>{}) + if(atWhoVisible){ + document.activeElement.id !== "blur_atWho" && document.getElementById("blur_atWho").focus(); + document.addEventListener("keydown",atWhoKeyDown()); } },[atWhoVisible]) @@ -260,52 +353,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //isCanAtme:只有issue和合并请求以及评论部分可以@他人操作 //当光标或选中内容时触发绑定@事件 isCanAtme && editorInstance.cm.on("focus", () => { - document.onkeydown = (e) => { - if (e.key === "@") { - // 输入@键后在对应的位置显示可选的项目成员 - setAtWhoVisible(true); - //获取光标位置 - const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style; - //设置弹框位置 - document.getElementById("at_who_list").style.top = (parseInt(cssStyle.getPropertyValue("top").replace("px",""))+60)+"px"; - document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+20)+"px"; - } else { - setAtWhoVisible(false); - } - //处理本来@了某人 -> 删掉 -> 撤回 的情况 - if(e.code === "KeyZ" && users.length != 0){ - const codemirror = editorInstance.cm; - let value = codemirror.getValue(); - //处理初始内容就自带@谁的情况 - if(initValue){ - const del = []; - users.map(item=>{ - if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ - //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 - del[del.length] = `@${item.username}`; - } - }) - del.length!=0 && del.map(str=>{ - value = value.replace(str,""); - }) - } - //判断value是否包含@符号 - if(value.indexOf("@") != -1){ - users.map(item =>{ - if(value.indexOf(item.username)!=-1 && value.charAt(value.indexOf(item.username)-1) ==="@"){ - //将此user的login存储到atWhoLoginList集合中 - const list = new Set(atWhoLoginList.current); - list.add(item.login); - atWhoLoginList.current = Array.from(list); - setAtWhoLoginListState(Array.from(list)); - } - }) - } - } - }; + document.addEventListener("keydown", mdKeyDown()); }); - editorInstance.cm.on("blur", () => { - document.onkeydown = null ; + isCanAtme && editorInstance.cm.on("blur", () => { + document.removeEventListener("keydown",mdKeyDown()); }); editorInstance.cm.on("change", (cm) => { onChange && onChange(cm.getValue()); @@ -428,6 +479,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
      + {atWhoVisible && atWhoList}
      From bcdb631c2006c3108ccdc8047b8361c2e1dd1860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Sat, 23 Oct 2021 19:43:58 +0800 Subject: [PATCH 15/28] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=BC=95=E5=85=A5?= =?UTF-8?q?=E7=9A=84js=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/jquery.atwho.min.css | 1 - public/js/jquery.atwho.js | 1215 ------------------------------- public/js/jquery.atwho.min.js | 1 - public/js/jquery.caret.js | 436 ----------- public/js/jquery.caret.min.js | 2 - 5 files changed, 1655 deletions(-) delete mode 100644 public/css/jquery.atwho.min.css delete mode 100644 public/js/jquery.atwho.js delete mode 100644 public/js/jquery.atwho.min.js delete mode 100644 public/js/jquery.caret.js delete mode 100644 public/js/jquery.caret.min.js diff --git a/public/css/jquery.atwho.min.css b/public/css/jquery.atwho.min.css deleted file mode 100644 index f770dc73..00000000 --- a/public/css/jquery.atwho.min.css +++ /dev/null @@ -1 +0,0 @@ -.atwho-view{position:absolute;top:0;left:0;display:none;margin-top:18px;background:#fff;color:#000;border:1px solid #DDD;border-radius:3px;box-shadow:0 0 5px rgba(0,0,0,.1);min-width:120px;z-index:11110!important}.atwho-view .atwho-header{padding:5px;margin:5px;cursor:pointer;border-bottom:solid 1px #eaeff1;color:#6f8092;font-size:11px;font-weight:700}.atwho-view .atwho-header .small{color:#6f8092;float:right;padding-top:2px;margin-right:-5px;font-size:12px;font-weight:400}.atwho-view .atwho-header:hover{cursor:default}.atwho-view .cur{background:#36F;color:#fff}.atwho-view .cur small{color:#fff}.atwho-view strong{color:#36F}.atwho-view .cur strong{color:#fff;font:700}.atwho-view ul{list-style:none;padding:0;margin:auto;max-height:200px;overflow-y:auto}.atwho-view ul li{display:block;padding:5px 10px;border-bottom:1px solid #DDD;cursor:pointer}.atwho-view small{font-size:smaller;color:#777;font-weight:400} \ No newline at end of file diff --git a/public/js/jquery.atwho.js b/public/js/jquery.atwho.js deleted file mode 100644 index 09ca40c1..00000000 --- a/public/js/jquery.atwho.js +++ /dev/null @@ -1,1215 +0,0 @@ -/** - * at.js - 1.5.4 - * Copyright (c) 2018 chord.luo ; - * Homepage: http://ichord.github.com/At.js - * License: MIT - */ -(function (root, factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module unless amdModuleId is set - define(["jquery"], function (a0) { - return (factory(a0)); - }); - } else if (typeof exports === 'object') { - // Node. Does not work with strict CommonJS, but - // only CommonJS-like environments that support module.exports, - // like Node. - module.exports = factory(require("jquery")); - } else { - factory(jQuery); - } -}(this, function ($) { -var DEFAULT_CALLBACKS, KEY_CODE; - -KEY_CODE = { - ESC: 27, - TAB: 9, - ENTER: 13, - CTRL: 17, - A: 65, - P: 80, - N: 78, - LEFT: 37, - UP: 38, - RIGHT: 39, - DOWN: 40, - BACKSPACE: 8, - SPACE: 32 -}; - -DEFAULT_CALLBACKS = { - beforeSave: function(data) { - return Controller.arrayToDefaultHash(data); - }, - matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) { - var _a, _y, match, regexp, space; - flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); - if (should_startWithSpace) { - flag = '(?:^|\\s)' + flag; - } - _a = decodeURI("%C3%80"); - _y = decodeURI("%C3%BF"); - space = acceptSpaceBar ? "\ " : ""; - regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_" + space + "\'\.\+\-]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi'); - match = regexp.exec(subtext); - if (match) { - return match[2] || match[1]; - } else { - return null; - } - }, - filter: function(query, data, searchKey) { - var _results, i, item, len; - _results = []; - for (i = 0, len = data.length; i < len; i++) { - item = data[i]; - if (~new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase())) { - _results.push(item); - } - } - return _results; - }, - remoteFilter: null, - sorter: function(query, items, searchKey) { - var _results, i, item, len; - if (!query) { - return items; - } - _results = []; - for (i = 0, len = items.length; i < len; i++) { - item = items[i]; - item.atwho_order = new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase()); - if (item.atwho_order > -1) { - _results.push(item); - } - } - return _results.sort(function(a, b) { - return a.atwho_order - b.atwho_order; - }); - }, - tplEval: function(tpl, map) { - var error, error1, template; - template = tpl; - try { - if (typeof tpl !== 'string') { - template = tpl(map); - } - return template.replace(/\$\{([^\}]*)\}/g, function(tag, key, pos) { - return map[key]; - }); - } catch (error1) { - error = error1; - return ""; - } - }, - highlighter: function(li, query) { - var regexp; - if (!query) { - return li; - } - regexp = new RegExp(">\\s*([^\<]*?)(" + query.replace("+", "\\+") + ")([^\<]*)\\s*<", 'ig'); - return li.replace(regexp, function(str, $1, $2, $3) { - return '> ' + $1 + '' + $2 + '' + $3 + ' <'; - }); - }, - beforeInsert: function(value, $li, e) { - return value; - }, - beforeReposition: function(offset) { - return offset; - }, - afterMatchFailed: function(at, el) {} -}; - -var App; - -App = (function() { - function App(inputor) { - this.currentFlag = null; - this.controllers = {}; - this.aliasMaps = {}; - this.$inputor = $(inputor); - this.setupRootElement(); - this.listen(); - } - - App.prototype.createContainer = function(doc) { - var ref; - if ((ref = this.$el) != null) { - ref.remove(); - } - return $(doc.body).append(this.$el = $("
      ")); - }; - - App.prototype.setupRootElement = function(iframe, asRoot) { - var error, error1; - if (asRoot == null) { - asRoot = false; - } - if (iframe) { - this.window = iframe.contentWindow; - this.document = iframe.contentDocument || this.window.document; - this.iframe = iframe; - } else { - this.document = this.$inputor[0].ownerDocument; - this.window = this.document.defaultView || this.document.parentWindow; - try { - this.iframe = this.window.frameElement; - } catch (error1) { - error = error1; - this.iframe = null; - if ($.fn.atwho.debug) { - throw new Error("iframe auto-discovery is failed.\nPlease use `setIframe` to set the target iframe manually.\n" + error); - } - } - } - return this.createContainer((this.iframeAsRoot = asRoot) ? this.document : document); - }; - - App.prototype.controller = function(at) { - var c, current, currentFlag, ref; - if (this.aliasMaps[at]) { - current = this.controllers[this.aliasMaps[at]]; - } else { - ref = this.controllers; - for (currentFlag in ref) { - c = ref[currentFlag]; - if (currentFlag === at) { - current = c; - break; - } - } - } - if (current) { - return current; - } else { - return this.controllers[this.currentFlag]; - } - }; - - App.prototype.setContextFor = function(at) { - this.currentFlag = at; - return this; - }; - - App.prototype.reg = function(flag, setting) { - var base, controller; - controller = (base = this.controllers)[flag] || (base[flag] = this.$inputor.is('[contentEditable]') ? new EditableController(this, flag) : new TextareaController(this, flag)); - if (setting.alias) { - this.aliasMaps[setting.alias] = flag; - } - controller.init(setting); - return this; - }; - - App.prototype.listen = function() { - return this.$inputor.on('compositionstart', (function(_this) { - return function(e) { - var ref; - if ((ref = _this.controller()) != null) { - ref.view.hide(); - } - _this.isComposing = true; - return null; - }; - })(this)).on('compositionend', (function(_this) { - return function(e) { - _this.isComposing = false; - setTimeout(function(e) { - return _this.dispatch(e); - }); - return null; - }; - })(this)).on('keyup.atwhoInner', (function(_this) { - return function(e) { - return _this.onKeyup(e); - }; - })(this)).on('keydown.atwhoInner', (function(_this) { - return function(e) { - return _this.onKeydown(e); - }; - })(this)).on('blur.atwhoInner', (function(_this) { - return function(e) { - var c; - if (c = _this.controller()) { - c.expectedQueryCBId = null; - return c.view.hide(e, c.getOpt("displayTimeout")); - } - }; - })(this)).on('click.atwhoInner', (function(_this) { - return function(e) { - return _this.dispatch(e); - }; - })(this)).on('scroll.atwhoInner', (function(_this) { - return function() { - var lastScrollTop; - lastScrollTop = _this.$inputor.scrollTop(); - return function(e) { - var currentScrollTop, ref; - currentScrollTop = e.target.scrollTop; - if (lastScrollTop !== currentScrollTop) { - if ((ref = _this.controller()) != null) { - ref.view.hide(e); - } - } - lastScrollTop = currentScrollTop; - return true; - }; - }; - })(this)()); - }; - - App.prototype.shutdown = function() { - var _, c, ref; - ref = this.controllers; - for (_ in ref) { - c = ref[_]; - c.destroy(); - delete this.controllers[_]; - } - this.$inputor.off('.atwhoInner'); - return this.$el.remove(); - }; - - App.prototype.dispatch = function(e) { - var _, c, ref, results; - if (void 0 === e) { - return; - } - ref = this.controllers; - results = []; - for (_ in ref) { - c = ref[_]; - results.push(c.lookUp(e)); - } - return results; - }; - - App.prototype.onKeyup = function(e) { - var ref; - switch (e.keyCode) { - case KEY_CODE.ESC: - e.preventDefault(); - if ((ref = this.controller()) != null) { - ref.view.hide(); - } - break; - case KEY_CODE.DOWN: - case KEY_CODE.UP: - case KEY_CODE.CTRL: - case KEY_CODE.ENTER: - $.noop(); - break; - case KEY_CODE.P: - case KEY_CODE.N: - if (!e.ctrlKey) { - this.dispatch(e); - } - break; - default: - this.dispatch(e); - } - }; - - App.prototype.onKeydown = function(e) { - var ref, view; - view = (ref = this.controller()) != null ? ref.view : void 0; - if (!(view && view.visible())) { - return; - } - switch (e.keyCode) { - case KEY_CODE.ESC: - e.preventDefault(); - view.hide(e); - break; - case KEY_CODE.UP: - e.preventDefault(); - view.prev(); - break; - case KEY_CODE.DOWN: - e.preventDefault(); - view.next(); - break; - case KEY_CODE.P: - if (!e.ctrlKey) { - return; - } - e.preventDefault(); - view.prev(); - break; - case KEY_CODE.N: - if (!e.ctrlKey) { - return; - } - e.preventDefault(); - view.next(); - break; - case KEY_CODE.TAB: - case KEY_CODE.ENTER: - case KEY_CODE.SPACE: - if (!view.visible()) { - return; - } - if (!this.controller().getOpt('spaceSelectsMatch') && e.keyCode === KEY_CODE.SPACE) { - return; - } - if (!this.controller().getOpt('tabSelectsMatch') && e.keyCode === KEY_CODE.TAB) { - return; - } - if (view.highlighted()) { - e.preventDefault(); - view.choose(e); - } else { - view.hide(e); - } - break; - default: - $.noop(); - } - }; - - return App; - -})(); - -var Controller, - slice = [].slice; - -Controller = (function() { - Controller.prototype.uid = function() { - return (Math.random().toString(16) + "000000000").substr(2, 8) + (new Date().getTime()); - }; - - function Controller(app, at1) { - this.app = app; - this.at = at1; - this.$inputor = this.app.$inputor; - this.id = this.$inputor[0].id || this.uid(); - this.expectedQueryCBId = null; - this.setting = null; - this.query = null; - this.pos = 0; - this.range = null; - if ((this.$el = $("#atwho-ground-" + this.id, this.app.$el)).length === 0) { - this.app.$el.append(this.$el = $("
      ")); - } - this.model = new Model(this); - this.view = new View(this); - } - - Controller.prototype.init = function(setting) { - this.setting = $.extend({}, this.setting || $.fn.atwho["default"], setting); - this.view.init(); - return this.model.reload(this.setting.data); - }; - - Controller.prototype.destroy = function() { - this.trigger('beforeDestroy'); - this.model.destroy(); - this.view.destroy(); - return this.$el.remove(); - }; - - Controller.prototype.callDefault = function() { - var args, error, error1, funcName; - funcName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; - try { - return DEFAULT_CALLBACKS[funcName].apply(this, args); - } catch (error1) { - error = error1; - return $.error(error + " Or maybe At.js doesn't have function " + funcName); - } - }; - - Controller.prototype.trigger = function(name, data) { - var alias, eventName; - if (data == null) { - data = []; - } - data.push(this); - alias = this.getOpt('alias'); - eventName = alias ? name + "-" + alias + ".atwho" : name + ".atwho"; - return this.$inputor.trigger(eventName, data); - }; - - Controller.prototype.callbacks = function(funcName) { - return this.getOpt("callbacks")[funcName] || DEFAULT_CALLBACKS[funcName]; - }; - - Controller.prototype.getOpt = function(at, default_value) { - var e, error1; - try { - return this.setting[at]; - } catch (error1) { - e = error1; - return null; - } - }; - - Controller.prototype.insertContentFor = function($li) { - var data, tpl; - tpl = this.getOpt('insertTpl'); - data = $.extend({}, $li.data('item-data'), { - 'atwho-at': this.at - }); - return this.callbacks("tplEval").call(this, tpl, data, "onInsert"); - }; - - Controller.prototype.renderView = function(data) { - var searchKey; - searchKey = this.getOpt("searchKey"); - data = this.callbacks("sorter").call(this, this.query.text, data.slice(0, 1001), searchKey); - return this.view.render(data.slice(0, this.getOpt('limit'))); - }; - - Controller.arrayToDefaultHash = function(data) { - var i, item, len, results; - if (!$.isArray(data)) { - return data; - } - results = []; - for (i = 0, len = data.length; i < len; i++) { - item = data[i]; - if ($.isPlainObject(item)) { - results.push(item); - } else { - results.push({ - name: item - }); - } - } - return results; - }; - - Controller.prototype.lookUp = function(e) { - var query, wait; - if (e && e.type === 'click' && !this.getOpt('lookUpOnClick')) { - return; - } - if (this.getOpt('suspendOnComposing') && this.app.isComposing) { - return; - } - query = this.catchQuery(e); - if (!query) { - this.expectedQueryCBId = null; - return query; - } - this.app.setContextFor(this.at); - if (wait = this.getOpt('delay')) { - this._delayLookUp(query, wait); - } else { - this._lookUp(query); - } - return query; - }; - - Controller.prototype._delayLookUp = function(query, wait) { - var now, remaining; - now = Date.now ? Date.now() : new Date().getTime(); - this.previousCallTime || (this.previousCallTime = now); - remaining = wait - (now - this.previousCallTime); - if ((0 < remaining && remaining < wait)) { - this.previousCallTime = now; - this._stopDelayedCall(); - return this.delayedCallTimeout = setTimeout((function(_this) { - return function() { - _this.previousCallTime = 0; - _this.delayedCallTimeout = null; - return _this._lookUp(query); - }; - })(this), wait); - } else { - this._stopDelayedCall(); - if (this.previousCallTime !== now) { - this.previousCallTime = 0; - } - return this._lookUp(query); - } - }; - - Controller.prototype._stopDelayedCall = function() { - if (this.delayedCallTimeout) { - clearTimeout(this.delayedCallTimeout); - return this.delayedCallTimeout = null; - } - }; - - Controller.prototype._generateQueryCBId = function() { - return {}; - }; - - Controller.prototype._lookUp = function(query) { - var _callback; - _callback = function(queryCBId, data) { - if (queryCBId !== this.expectedQueryCBId) { - return; - } - if (data && data.length > 0) { - return this.renderView(this.constructor.arrayToDefaultHash(data)); - } else { - return this.view.hide(); - } - }; - this.expectedQueryCBId = this._generateQueryCBId(); - return this.model.query(query.text, $.proxy(_callback, this, this.expectedQueryCBId)); - }; - - return Controller; - -})(); - -var TextareaController, - extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; - -TextareaController = (function(superClass) { - extend(TextareaController, superClass); - - function TextareaController() { - return TextareaController.__super__.constructor.apply(this, arguments); - } - - TextareaController.prototype.catchQuery = function() { - var caretPos, content, end, isString, query, start, subtext; - content = this.$inputor.val(); - caretPos = this.$inputor.caret('pos', { - iframe: this.app.iframe - }); - subtext = content.slice(0, caretPos); - query = this.callbacks("matcher").call(this, this.at, subtext, this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar")); - isString = typeof query === 'string'; - if (isString && query.length < this.getOpt('minLen', 0)) { - return; - } - if (isString && query.length <= this.getOpt('maxLen', 20)) { - start = caretPos - query.length; - end = start + query.length; - this.pos = start; - query = { - 'text': query, - 'headPos': start, - 'endPos': end - }; - this.trigger("matched", [this.at, query.text]); - } else { - query = null; - this.view.hide(); - } - return this.query = query; - }; - - TextareaController.prototype.rect = function() { - var c, iframeOffset, scaleBottom; - if (!(c = this.$inputor.caret('offset', this.pos - 1, { - iframe: this.app.iframe - }))) { - return; - } - if (this.app.iframe && !this.app.iframeAsRoot) { - iframeOffset = $(this.app.iframe).offset(); - c.left += iframeOffset.left; - c.top += iframeOffset.top; - } - scaleBottom = this.app.document.selection ? 0 : 2; - return { - left: c.left, - top: c.top, - bottom: c.top + c.height + scaleBottom - }; - }; - - TextareaController.prototype.insert = function(content, $li) { - var $inputor, source, startStr, suffix, text; - $inputor = this.$inputor; - source = $inputor.val(); - startStr = source.slice(0, Math.max(this.query.headPos - this.at.length, 0)); - suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || " "; - content += suffix; - text = "" + startStr + content + (source.slice(this.query['endPos'] || 0)); - $inputor.val(text); - $inputor.caret('pos', startStr.length + content.length, { - iframe: this.app.iframe - }); - if (!$inputor.is(':focus')) { - $inputor.focus(); - } - return $inputor.change(); - }; - - return TextareaController; - -})(Controller); - -var EditableController, - extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; - -EditableController = (function(superClass) { - extend(EditableController, superClass); - - function EditableController() { - return EditableController.__super__.constructor.apply(this, arguments); - } - - EditableController.prototype._getRange = function() { - var sel; - sel = this.app.window.getSelection(); - if (sel.rangeCount > 0) { - return sel.getRangeAt(0); - } - }; - - EditableController.prototype._setRange = function(position, node, range) { - if (range == null) { - range = this._getRange(); - } - if (!(range && node)) { - return; - } - node = $(node)[0]; - if (position === 'after') { - range.setEndAfter(node); - range.setStartAfter(node); - } else { - range.setEndBefore(node); - range.setStartBefore(node); - } - range.collapse(false); - return this._clearRange(range); - }; - - EditableController.prototype._clearRange = function(range) { - var sel; - if (range == null) { - range = this._getRange(); - } - sel = this.app.window.getSelection(); - if (this.ctrl_a_pressed == null) { - sel.removeAllRanges(); - return sel.addRange(range); - } - }; - - EditableController.prototype._movingEvent = function(e) { - var ref; - return e.type === 'click' || ((ref = e.which) === KEY_CODE.RIGHT || ref === KEY_CODE.LEFT || ref === KEY_CODE.UP || ref === KEY_CODE.DOWN); - }; - - EditableController.prototype._unwrap = function(node) { - var next; - node = $(node).unwrap().get(0); - if ((next = node.nextSibling) && next.nodeValue) { - node.nodeValue += next.nodeValue; - $(next).remove(); - } - return node; - }; - - EditableController.prototype.catchQuery = function(e) { - var $inserted, $query, _range, index, inserted, isString, lastNode, matched, offset, query, query_content, range; - if (!(range = this._getRange())) { - return; - } - if (!range.collapsed) { - return; - } - if (e.which === KEY_CODE.ENTER) { - ($query = $(range.startContainer).closest('.atwho-query')).contents().unwrap(); - if ($query.is(':empty')) { - $query.remove(); - } - ($query = $(".atwho-query", this.app.document)).text($query.text()).contents().last().unwrap(); - this._clearRange(); - return; - } - if (/firefox/i.test(navigator.userAgent)) { - if ($(range.startContainer).is(this.$inputor)) { - this._clearRange(); - return; - } - if (e.which === KEY_CODE.BACKSPACE && range.startContainer.nodeType === document.ELEMENT_NODE && (offset = range.startOffset - 1) >= 0) { - _range = range.cloneRange(); - _range.setStart(range.startContainer, offset); - if ($(_range.cloneContents()).contents().last().is('.atwho-inserted')) { - inserted = $(range.startContainer).contents().get(offset); - this._setRange('after', $(inserted).contents().last()); - } - } else if (e.which === KEY_CODE.LEFT && range.startContainer.nodeType === document.TEXT_NODE) { - $inserted = $(range.startContainer.previousSibling); - if ($inserted.is('.atwho-inserted') && range.startOffset === 0) { - this._setRange('after', $inserted.contents().last()); - } - } - } - $(range.startContainer).closest('.atwho-inserted').addClass('atwho-query').siblings().removeClass('atwho-query'); - if (($query = $(".atwho-query", this.app.document)).length > 0 && $query.is(':empty') && $query.text().length === 0) { - $query.remove(); - } - if (!this._movingEvent(e)) { - $query.removeClass('atwho-inserted'); - } - if ($query.length > 0) { - switch (e.which) { - case KEY_CODE.LEFT: - this._setRange('before', $query.get(0), range); - $query.removeClass('atwho-query'); - return; - case KEY_CODE.RIGHT: - this._setRange('after', $query.get(0).nextSibling, range); - $query.removeClass('atwho-query'); - return; - } - } - if ($query.length > 0 && (query_content = $query.attr('data-atwho-at-query'))) { - $query.empty().html(query_content).attr('data-atwho-at-query', null); - this._setRange('after', $query.get(0), range); - } - _range = range.cloneRange(); - _range.setStart(range.startContainer, 0); - matched = this.callbacks("matcher").call(this, this.at, _range.toString(), this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar")); - isString = typeof matched === 'string'; - if ($query.length === 0 && isString && (index = range.startOffset - this.at.length - matched.length) >= 0) { - range.setStart(range.startContainer, index); - $query = $('', this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass('atwho-query'); - range.surroundContents($query.get(0)); - lastNode = $query.contents().last().get(0); - if (lastNode) { - if (/firefox/i.test(navigator.userAgent)) { - range.setStart(lastNode, lastNode.length); - range.setEnd(lastNode, lastNode.length); - this._clearRange(range); - } else { - this._setRange('after', lastNode, range); - } - } - } - if (isString && matched.length < this.getOpt('minLen', 0)) { - return; - } - if (isString && matched.length <= this.getOpt('maxLen', 20)) { - query = { - text: matched, - el: $query - }; - this.trigger("matched", [this.at, query.text]); - return this.query = query; - } else { - this.view.hide(); - this.query = { - el: $query - }; - if ($query.text().indexOf(this.at) >= 0) { - if (this._movingEvent(e) && $query.hasClass('atwho-inserted')) { - $query.removeClass('atwho-query'); - } else if (false !== this.callbacks('afterMatchFailed').call(this, this.at, $query)) { - this._setRange("after", this._unwrap($query.text($query.text()).contents().first())); - } - } - return null; - } - }; - - EditableController.prototype.rect = function() { - var $iframe, iframeOffset, rect; - rect = this.query.el.offset(); - if (!(rect && this.query.el[0].getClientRects().length)) { - return; - } - if (this.app.iframe && !this.app.iframeAsRoot) { - iframeOffset = ($iframe = $(this.app.iframe)).offset(); - rect.left += iframeOffset.left - this.$inputor.scrollLeft(); - rect.top += iframeOffset.top - this.$inputor.scrollTop(); - } - rect.bottom = rect.top + this.query.el.height(); - return rect; - }; - - EditableController.prototype.insert = function(content, $li) { - var data, overrides, range, suffix, suffixNode; - if (!this.$inputor.is(':focus')) { - this.$inputor.focus(); - } - overrides = this.getOpt('functionOverrides'); - if (overrides.insert) { - return overrides.insert.call(this, content, $li); - } - suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || "\u00A0"; - data = $li.data('item-data'); - this.query.el.removeClass('atwho-query').addClass('atwho-inserted').html(content).attr('data-atwho-at-query', "" + data['atwho-at'] + this.query.text).attr('contenteditable', "false"); - if (range = this._getRange()) { - if (this.query.el.length) { - range.setEndAfter(this.query.el[0]); - } - range.collapse(false); - range.insertNode(suffixNode = this.app.document.createTextNode("" + suffix)); - this._setRange('after', suffixNode, range); - } - if (!this.$inputor.is(':focus')) { - this.$inputor.focus(); - } - return this.$inputor.change(); - }; - - return EditableController; - -})(Controller); - -var Model; - -Model = (function() { - function Model(context) { - this.context = context; - this.at = this.context.at; - this.storage = this.context.$inputor; - } - - Model.prototype.destroy = function() { - return this.storage.data(this.at, null); - }; - - Model.prototype.saved = function() { - return this.fetch() > 0; - }; - - Model.prototype.query = function(query, callback) { - var _remoteFilter, data, searchKey; - data = this.fetch(); - searchKey = this.context.getOpt("searchKey"); - data = this.context.callbacks('filter').call(this.context, query, data, searchKey) || []; - _remoteFilter = this.context.callbacks('remoteFilter'); - if (data.length > 0 || (!_remoteFilter && data.length === 0)) { - return callback(data); - } else { - return _remoteFilter.call(this.context, query, callback); - } - }; - - Model.prototype.fetch = function() { - return this.storage.data(this.at) || []; - }; - - Model.prototype.save = function(data) { - return this.storage.data(this.at, this.context.callbacks("beforeSave").call(this.context, data || [])); - }; - - Model.prototype.load = function(data) { - if (!(this.saved() || !data)) { - return this._load(data); - } - }; - - Model.prototype.reload = function(data) { - return this._load(data); - }; - - Model.prototype._load = function(data) { - if (typeof data === "string") { - return $.ajax(data, { - dataType: "json" - }).done((function(_this) { - return function(data) { - return _this.save(data); - }; - })(this)); - } else { - return this.save(data); - } - }; - - return Model; - -})(); - -var View; - -View = (function() { - function View(context) { - this.context = context; - this.$el = $("
        "); - this.$elUl = this.$el.children(); - this.timeoutID = null; - this.context.$el.append(this.$el); - this.bindEvent(); - } - - View.prototype.init = function() { - var header_tpl, id; - id = this.context.getOpt("alias") || this.context.at.charCodeAt(0); - header_tpl = this.context.getOpt("headerTpl"); - if (header_tpl && this.$el.children().length === 1) { - this.$el.prepend(header_tpl); - } - return this.$el.attr({ - 'id': "at-view-" + id - }); - }; - - View.prototype.destroy = function() { - return this.$el.remove(); - }; - - View.prototype.bindEvent = function() { - var $menu, lastCoordX, lastCoordY; - $menu = this.$el.find('ul'); - lastCoordX = 0; - lastCoordY = 0; - return $menu.on('mousemove.atwho-view', 'li', (function(_this) { - return function(e) { - var $cur; - if (lastCoordX === e.clientX && lastCoordY === e.clientY) { - return; - } - lastCoordX = e.clientX; - lastCoordY = e.clientY; - $cur = $(e.currentTarget); - if ($cur.hasClass('cur')) { - return; - } - $menu.find('.cur').removeClass('cur'); - return $cur.addClass('cur'); - }; - })(this)).on('click.atwho-view', 'li', (function(_this) { - return function(e) { - $menu.find('.cur').removeClass('cur'); - $(e.currentTarget).addClass('cur'); - _this.choose(e); - return e.preventDefault(); - }; - })(this)); - }; - - View.prototype.visible = function() { - return $.expr.filters.visible(this.$el[0]); - }; - - View.prototype.highlighted = function() { - return this.$el.find(".cur").length > 0; - }; - - View.prototype.choose = function(e) { - var $li, content; - if (($li = this.$el.find(".cur")).length) { - content = this.context.insertContentFor($li); - this.context._stopDelayedCall(); - this.context.insert(this.context.callbacks("beforeInsert").call(this.context, content, $li, e), $li); - this.context.trigger("inserted", [$li, e]); - this.hide(e); - } - if (this.context.getOpt("hideWithoutSuffix")) { - return this.stopShowing = true; - } - }; - - View.prototype.reposition = function(rect) { - var _window, offset, overflowOffset, ref; - _window = this.context.app.iframeAsRoot ? this.context.app.window : window; - if (rect.bottom + this.$el.height() - $(_window).scrollTop() > $(_window).height()) { - rect.bottom = rect.top - this.$el.height(); - } - if (rect.left > (overflowOffset = $(_window).width() - this.$el.width() - 5)) { - rect.left = overflowOffset; - } - offset = { - left: rect.left, - top: rect.bottom - }; - if ((ref = this.context.callbacks("beforeReposition")) != null) { - ref.call(this.context, offset); - } - this.$el.offset(offset); - return this.context.trigger("reposition", [offset]); - }; - - View.prototype.next = function() { - var cur, next, nextEl, offset; - cur = this.$el.find('.cur').removeClass('cur'); - next = cur.next(); - if (!next.length) { - next = this.$el.find('li:first'); - } - next.addClass('cur'); - nextEl = next[0]; - offset = nextEl.offsetTop + nextEl.offsetHeight + (nextEl.nextSibling ? nextEl.nextSibling.offsetHeight : 0); - return this.scrollTop(Math.max(0, offset - this.$el.height())); - }; - - View.prototype.prev = function() { - var cur, offset, prev, prevEl; - cur = this.$el.find('.cur').removeClass('cur'); - prev = cur.prev(); - if (!prev.length) { - prev = this.$el.find('li:last'); - } - prev.addClass('cur'); - prevEl = prev[0]; - offset = prevEl.offsetTop + prevEl.offsetHeight + (prevEl.nextSibling ? prevEl.nextSibling.offsetHeight : 0); - return this.scrollTop(Math.max(0, offset - this.$el.height())); - }; - - View.prototype.scrollTop = function(scrollTop) { - var scrollDuration; - scrollDuration = this.context.getOpt('scrollDuration'); - if (scrollDuration) { - return this.$elUl.animate({ - scrollTop: scrollTop - }, scrollDuration); - } else { - return this.$elUl.scrollTop(scrollTop); - } - }; - - View.prototype.show = function() { - var rect; - if (this.stopShowing) { - this.stopShowing = false; - return; - } - if (!this.visible()) { - this.$el.show(); - this.$el.scrollTop(0); - this.context.trigger('shown'); - } - if (rect = this.context.rect()) { - return this.reposition(rect); - } - }; - - View.prototype.hide = function(e, time) { - var callback; - if (!this.visible()) { - return; - } - if (isNaN(time)) { - this.$el.hide(); - return this.context.trigger('hidden', [e]); - } else { - callback = (function(_this) { - return function() { - return _this.hide(); - }; - })(this); - clearTimeout(this.timeoutID); - return this.timeoutID = setTimeout(callback, time); - } - }; - - View.prototype.render = function(list) { - var $li, $ul, i, item, len, li, tpl; - if (!($.isArray(list) && list.length > 0)) { - this.hide(); - return; - } - this.$el.find('ul').empty(); - $ul = this.$el.find('ul'); - tpl = this.context.getOpt('displayTpl'); - for (i = 0, len = list.length; i < len; i++) { - item = list[i]; - item = $.extend({}, item, { - 'atwho-at': this.context.at - }); - li = this.context.callbacks("tplEval").call(this.context, tpl, item, "onDisplay"); - $li = $(this.context.callbacks("highlighter").call(this.context, li, this.context.query.text)); - $li.data("item-data", item); - $ul.append($li); - } - this.show(); - if (this.context.getOpt('highlightFirst')) { - return $ul.find("li:first").addClass("cur"); - } - }; - - return View; - -})(); - -var Api; - -Api = { - load: function(at, data) { - var c; - if (c = this.controller(at)) { - return c.model.load(data); - } - }, - isSelecting: function() { - var ref; - return !!((ref = this.controller()) != null ? ref.view.visible() : void 0); - }, - hide: function() { - var ref; - return (ref = this.controller()) != null ? ref.view.hide() : void 0; - }, - reposition: function() { - var c; - if (c = this.controller()) { - return c.view.reposition(c.rect()); - } - }, - setIframe: function(iframe, asRoot) { - this.setupRootElement(iframe, asRoot); - return null; - }, - run: function() { - return this.dispatch(); - }, - destroy: function() { - this.shutdown(); - return this.$inputor.data('atwho', null); - } -}; - -$.fn.atwho = function(method) { - var _args, result; - _args = arguments; - result = null; - this.filter('textarea, input, [contenteditable=""], [contenteditable=true]').each(function() { - var $this, app; - if (!(app = ($this = $(this)).data("atwho"))) { - $this.data('atwho', (app = new App(this))); - } - if (typeof method === 'object' || !method) { - return app.reg(method.at, method); - } else if (Api[method] && app) { - return result = Api[method].apply(app, Array.prototype.slice.call(_args, 1)); - } else { - return $.error("Method " + method + " does not exist on jQuery.atwho"); - } - }); - if (result != null) { - return result; - } else { - return this; - } -}; - -$.fn.atwho["default"] = { - at: void 0, - alias: void 0, - data: null, - displayTpl: "
      • ${name}
      • ", - insertTpl: "${atwho-at}${name}", - headerTpl: null, - callbacks: DEFAULT_CALLBACKS, - functionOverrides: {}, - searchKey: "name", - suffix: void 0, - hideWithoutSuffix: false, - startWithSpace: true, - acceptSpaceBar: false, - highlightFirst: true, - limit: 5, - maxLen: 20, - minLen: 0, - displayTimeout: 300, - delay: null, - spaceSelectsMatch: false, - tabSelectsMatch: true, - editableAtwhoQueryAttrs: {}, - scrollDuration: 150, - suspendOnComposing: true, - lookUpOnClick: true -}; - -$.fn.atwho.debug = false; - -})); diff --git a/public/js/jquery.atwho.min.js b/public/js/jquery.atwho.min.js deleted file mode 100644 index 857bb931..00000000 --- a/public/js/jquery.atwho.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(t,e){"function"==typeof define&&define.amd?define(["jquery"],function(t){return e(t)}):"object"==typeof exports?module.exports=e(require("jquery")):e(jQuery)}(this,function(t){var e,i;i={ESC:27,TAB:9,ENTER:13,CTRL:17,A:65,P:80,N:78,LEFT:37,UP:38,RIGHT:39,DOWN:40,BACKSPACE:8,SPACE:32},e={beforeSave:function(t){return r.arrayToDefaultHash(t)},matcher:function(t,e,i,n){var r,o,s,a,h;return t=t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&"),i&&(t="(?:^|\\s)"+t),r=decodeURI("%C3%80"),o=decodeURI("%C3%BF"),h=n?" ":"",a=new RegExp(t+"([A-Za-z"+r+"-"+o+"0-9_"+h+"'.+-]*)$|"+t+"([^\\x00-\\xff]*)$","gi"),s=a.exec(e),s?s[2]||s[1]:null},filter:function(t,e,i){var n,r,o,s;for(n=[],r=0,s=e.length;s>r;r++)o=e[r],~new String(o[i]).toLowerCase().indexOf(t.toLowerCase())&&n.push(o);return n},remoteFilter:null,sorter:function(t,e,i){var n,r,o,s;if(!t)return e;for(n=[],r=0,s=e.length;s>r;r++)o=e[r],o.atwho_order=new String(o[i]).toLowerCase().indexOf(t.toLowerCase()),o.atwho_order>-1&&n.push(o);return n.sort(function(t,e){return t.atwho_order-e.atwho_order})},tplEval:function(t,e){var i,n,r;r=t;try{return"string"!=typeof t&&(r=t(e)),r.replace(/\$\{([^\}]*)\}/g,function(t,i,n){return e[i]})}catch(n){return i=n,""}},highlighter:function(t,e){var i;return e?(i=new RegExp(">\\s*([^<]*?)("+e.replace("+","\\+")+")([^<]*)\\s*<","ig"),t.replace(i,function(t,e,i,n){return"> "+e+""+i+""+n+" <"})):t},beforeInsert:function(t,e,i){return t},beforeReposition:function(t){return t},afterMatchFailed:function(t,e){}};var n;n=function(){function e(e){this.currentFlag=null,this.controllers={},this.aliasMaps={},this.$inputor=t(e),this.setupRootElement(),this.listen()}return e.prototype.createContainer=function(e){var i;return null!=(i=this.$el)&&i.remove(),t(e.body).append(this.$el=t("
        "))},e.prototype.setupRootElement=function(e,i){var n,r;if(null==i&&(i=!1),e)this.window=e.contentWindow,this.document=e.contentDocument||this.window.document,this.iframe=e;else{this.document=this.$inputor[0].ownerDocument,this.window=this.document.defaultView||this.document.parentWindow;try{this.iframe=this.window.frameElement}catch(r){if(n=r,this.iframe=null,t.fn.atwho.debug)throw new Error("iframe auto-discovery is failed.\nPlease use `setIframe` to set the target iframe manually.\n"+n)}}return this.createContainer((this.iframeAsRoot=i)?this.document:document)},e.prototype.controller=function(t){var e,i,n,r;if(this.aliasMaps[t])i=this.controllers[this.aliasMaps[t]];else{r=this.controllers;for(n in r)if(e=r[n],n===t){i=e;break}}return i?i:this.controllers[this.currentFlag]},e.prototype.setContextFor=function(t){return this.currentFlag=t,this},e.prototype.reg=function(t,e){var i,n;return n=(i=this.controllers)[t]||(i[t]=this.$inputor.is("[contentEditable]")?new l(this,t):new s(this,t)),e.alias&&(this.aliasMaps[e.alias]=t),n.init(e),this},e.prototype.listen=function(){return this.$inputor.on("compositionstart",function(t){return function(e){var i;return null!=(i=t.controller())&&i.view.hide(),t.isComposing=!0,null}}(this)).on("compositionend",function(t){return function(e){return t.isComposing=!1,setTimeout(function(e){return t.dispatch(e)}),null}}(this)).on("keyup.atwhoInner",function(t){return function(e){return t.onKeyup(e)}}(this)).on("keydown.atwhoInner",function(t){return function(e){return t.onKeydown(e)}}(this)).on("blur.atwhoInner",function(t){return function(e){var i;return(i=t.controller())?(i.expectedQueryCBId=null,i.view.hide(e,i.getOpt("displayTimeout"))):void 0}}(this)).on("click.atwhoInner",function(t){return function(e){return t.dispatch(e)}}(this)).on("scroll.atwhoInner",function(t){return function(){var e;return e=t.$inputor.scrollTop(),function(i){var n,r;return n=i.target.scrollTop,e!==n&&null!=(r=t.controller())&&r.view.hide(i),e=n,!0}}}(this)())},e.prototype.shutdown=function(){var t,e,i;i=this.controllers;for(t in i)e=i[t],e.destroy(),delete this.controllers[t];return this.$inputor.off(".atwhoInner"),this.$el.remove()},e.prototype.dispatch=function(t){var e,i,n,r;if(void 0!==t){n=this.controllers,r=[];for(e in n)i=n[e],r.push(i.lookUp(t));return r}},e.prototype.onKeyup=function(e){var n;switch(e.keyCode){case i.ESC:e.preventDefault(),null!=(n=this.controller())&&n.view.hide();break;case i.DOWN:case i.UP:case i.CTRL:case i.ENTER:t.noop();break;case i.P:case i.N:e.ctrlKey||this.dispatch(e);break;default:this.dispatch(e)}},e.prototype.onKeydown=function(e){var n,r;if(r=null!=(n=this.controller())?n.view:void 0,r&&r.visible())switch(e.keyCode){case i.ESC:e.preventDefault(),r.hide(e);break;case i.UP:e.preventDefault(),r.prev();break;case i.DOWN:e.preventDefault(),r.next();break;case i.P:if(!e.ctrlKey)return;e.preventDefault(),r.prev();break;case i.N:if(!e.ctrlKey)return;e.preventDefault(),r.next();break;case i.TAB:case i.ENTER:case i.SPACE:if(!r.visible())return;if(!this.controller().getOpt("spaceSelectsMatch")&&e.keyCode===i.SPACE)return;if(!this.controller().getOpt("tabSelectsMatch")&&e.keyCode===i.TAB)return;r.highlighted()?(e.preventDefault(),r.choose(e)):r.hide(e);break;default:t.noop()}},e}();var r,o=[].slice;r=function(){function i(e,i){this.app=e,this.at=i,this.$inputor=this.app.$inputor,this.id=this.$inputor[0].id||this.uid(),this.expectedQueryCBId=null,this.setting=null,this.query=null,this.pos=0,this.range=null,0===(this.$el=t("#atwho-ground-"+this.id,this.app.$el)).length&&this.app.$el.append(this.$el=t("
        ")),this.model=new u(this),this.view=new c(this)}return i.prototype.uid=function(){return(Math.random().toString(16)+"000000000").substr(2,8)+(new Date).getTime()},i.prototype.init=function(e){return this.setting=t.extend({},this.setting||t.fn.atwho["default"],e),this.view.init(),this.model.reload(this.setting.data)},i.prototype.destroy=function(){return this.trigger("beforeDestroy"),this.model.destroy(),this.view.destroy(),this.$el.remove()},i.prototype.callDefault=function(){var i,n,r,s;s=arguments[0],i=2<=arguments.length?o.call(arguments,1):[];try{return e[s].apply(this,i)}catch(r){return n=r,t.error(n+" Or maybe At.js doesn't have function "+s)}},i.prototype.trigger=function(t,e){var i,n;return null==e&&(e=[]),e.push(this),i=this.getOpt("alias"),n=i?t+"-"+i+".atwho":t+".atwho",this.$inputor.trigger(n,e)},i.prototype.callbacks=function(t){return this.getOpt("callbacks")[t]||e[t]},i.prototype.getOpt=function(t,e){var i,n;try{return this.setting[t]}catch(n){return i=n,null}},i.prototype.insertContentFor=function(e){var i,n;return n=this.getOpt("insertTpl"),i=t.extend({},e.data("item-data"),{"atwho-at":this.at}),this.callbacks("tplEval").call(this,n,i,"onInsert")},i.prototype.renderView=function(t){var e;return e=this.getOpt("searchKey"),t=this.callbacks("sorter").call(this,this.query.text,t.slice(0,1001),e),this.view.render(t.slice(0,this.getOpt("limit")))},i.arrayToDefaultHash=function(e){var i,n,r,o;if(!t.isArray(e))return e;for(o=[],i=0,r=e.length;r>i;i++)n=e[i],t.isPlainObject(n)?o.push(n):o.push({name:n});return o},i.prototype.lookUp=function(t){var e,i;if((!t||"click"!==t.type||this.getOpt("lookUpOnClick"))&&(!this.getOpt("suspendOnComposing")||!this.app.isComposing))return(e=this.catchQuery(t))?(this.app.setContextFor(this.at),(i=this.getOpt("delay"))?this._delayLookUp(e,i):this._lookUp(e),e):(this.expectedQueryCBId=null,e)},i.prototype._delayLookUp=function(t,e){var i,n;return i=Date.now?Date.now():(new Date).getTime(),this.previousCallTime||(this.previousCallTime=i),n=e-(i-this.previousCallTime),n>0&&e>n?(this.previousCallTime=i,this._stopDelayedCall(),this.delayedCallTimeout=setTimeout(function(e){return function(){return e.previousCallTime=0,e.delayedCallTimeout=null,e._lookUp(t)}}(this),e)):(this._stopDelayedCall(),this.previousCallTime!==i&&(this.previousCallTime=0),this._lookUp(t))},i.prototype._stopDelayedCall=function(){return this.delayedCallTimeout?(clearTimeout(this.delayedCallTimeout),this.delayedCallTimeout=null):void 0},i.prototype._generateQueryCBId=function(){return{}},i.prototype._lookUp=function(e){var i;return i=function(t,e){return t===this.expectedQueryCBId?e&&e.length>0?this.renderView(this.constructor.arrayToDefaultHash(e)):this.view.hide():void 0},this.expectedQueryCBId=this._generateQueryCBId(),this.model.query(e.text,t.proxy(i,this,this.expectedQueryCBId))},i}();var s,a=function(t,e){function i(){this.constructor=t}for(var n in e)h.call(e,n)&&(t[n]=e[n]);return i.prototype=e.prototype,t.prototype=new i,t.__super__=e.prototype,t},h={}.hasOwnProperty;s=function(e){function i(){return i.__super__.constructor.apply(this,arguments)}return a(i,e),i.prototype.catchQuery=function(){var t,e,i,n,r,o,s;return e=this.$inputor.val(),t=this.$inputor.caret("pos",{iframe:this.app.iframe}),s=e.slice(0,t),r=this.callbacks("matcher").call(this,this.at,s,this.getOpt("startWithSpace"),this.getOpt("acceptSpaceBar")),n="string"==typeof r,n&&r.length0?t.getRangeAt(0):void 0},n.prototype._setRange=function(e,i,n){return null==n&&(n=this._getRange()),n&&i?(i=t(i)[0],"after"===e?(n.setEndAfter(i),n.setStartAfter(i)):(n.setEndBefore(i),n.setStartBefore(i)),n.collapse(!1),this._clearRange(n)):void 0},n.prototype._clearRange=function(t){var e;return null==t&&(t=this._getRange()),e=this.app.window.getSelection(),null==this.ctrl_a_pressed?(e.removeAllRanges(),e.addRange(t)):void 0},n.prototype._movingEvent=function(t){var e;return"click"===t.type||(e=t.which)===i.RIGHT||e===i.LEFT||e===i.UP||e===i.DOWN},n.prototype._unwrap=function(e){var i;return e=t(e).unwrap().get(0),(i=e.nextSibling)&&i.nodeValue&&(e.nodeValue+=i.nodeValue,t(i).remove()),e},n.prototype.catchQuery=function(e){var n,r,o,s,a,h,l,u,c,p,f,d;if((d=this._getRange())&&d.collapsed){if(e.which===i.ENTER)return(r=t(d.startContainer).closest(".atwho-query")).contents().unwrap(),r.is(":empty")&&r.remove(),(r=t(".atwho-query",this.app.document)).text(r.text()).contents().last().unwrap(),void this._clearRange();if(/firefox/i.test(navigator.userAgent)){if(t(d.startContainer).is(this.$inputor))return void this._clearRange();e.which===i.BACKSPACE&&d.startContainer.nodeType===document.ELEMENT_NODE&&(c=d.startOffset-1)>=0?(o=d.cloneRange(),o.setStart(d.startContainer,c),t(o.cloneContents()).contents().last().is(".atwho-inserted")&&(a=t(d.startContainer).contents().get(c),this._setRange("after",t(a).contents().last()))):e.which===i.LEFT&&d.startContainer.nodeType===document.TEXT_NODE&&(n=t(d.startContainer.previousSibling),n.is(".atwho-inserted")&&0===d.startOffset&&this._setRange("after",n.contents().last()))}if(t(d.startContainer).closest(".atwho-inserted").addClass("atwho-query").siblings().removeClass("atwho-query"),(r=t(".atwho-query",this.app.document)).length>0&&r.is(":empty")&&0===r.text().length&&r.remove(),this._movingEvent(e)||r.removeClass("atwho-inserted"),r.length>0)switch(e.which){case i.LEFT:return this._setRange("before",r.get(0),d),void r.removeClass("atwho-query");case i.RIGHT:return this._setRange("after",r.get(0).nextSibling,d),void r.removeClass("atwho-query")}if(r.length>0&&(f=r.attr("data-atwho-at-query"))&&(r.empty().html(f).attr("data-atwho-at-query",null),this._setRange("after",r.get(0),d)),o=d.cloneRange(),o.setStart(d.startContainer,0),u=this.callbacks("matcher").call(this,this.at,o.toString(),this.getOpt("startWithSpace"),this.getOpt("acceptSpaceBar")),h="string"==typeof u,0===r.length&&h&&(s=d.startOffset-this.at.length-u.length)>=0&&(d.setStart(d.startContainer,s),r=t("",this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass("atwho-query"),d.surroundContents(r.get(0)),l=r.contents().last().get(0),l&&(/firefox/i.test(navigator.userAgent)?(d.setStart(l,l.length),d.setEnd(l,l.length),this._clearRange(d)):this._setRange("after",l,d))),!(h&&u.length=0&&(this._movingEvent(e)&&r.hasClass("atwho-inserted")?r.removeClass("atwho-query"):!1!==this.callbacks("afterMatchFailed").call(this,this.at,r)&&this._setRange("after",this._unwrap(r.text(r.text()).contents().first()))),null)}},n.prototype.rect=function(){var e,i,n;return n=this.query.el.offset(),n&&this.query.el[0].getClientRects().length?(this.app.iframe&&!this.app.iframeAsRoot&&(i=(e=t(this.app.iframe)).offset(),n.left+=i.left-this.$inputor.scrollLeft(),n.top+=i.top-this.$inputor.scrollTop()),n.bottom=n.top+this.query.el.height(),n):void 0},n.prototype.insert=function(t,e){var i,n,r,o,s;return this.$inputor.is(":focus")||this.$inputor.focus(),n=this.getOpt("functionOverrides"),n.insert?n.insert.call(this,t,e):(o=""===(o=this.getOpt("suffix"))?o:o||" ",i=e.data("item-data"),this.query.el.removeClass("atwho-query").addClass("atwho-inserted").html(t).attr("data-atwho-at-query",""+i["atwho-at"]+this.query.text).attr("contenteditable","false"),(r=this._getRange())&&(this.query.el.length&&r.setEndAfter(this.query.el[0]),r.collapse(!1),r.insertNode(s=this.app.document.createTextNode(""+o)),this._setRange("after",s,r)),this.$inputor.is(":focus")||this.$inputor.focus(),this.$inputor.change())},n}(r);var u;u=function(){function e(t){this.context=t,this.at=this.context.at,this.storage=this.context.$inputor}return e.prototype.destroy=function(){return this.storage.data(this.at,null)},e.prototype.saved=function(){return this.fetch()>0},e.prototype.query=function(t,e){var i,n,r;return n=this.fetch(),r=this.context.getOpt("searchKey"),n=this.context.callbacks("filter").call(this.context,t,n,r)||[],i=this.context.callbacks("remoteFilter"),n.length>0||!i&&0===n.length?e(n):i.call(this.context,t,e)},e.prototype.fetch=function(){return this.storage.data(this.at)||[]},e.prototype.save=function(t){return this.storage.data(this.at,this.context.callbacks("beforeSave").call(this.context,t||[]))},e.prototype.load=function(t){return!this.saved()&&t?this._load(t):void 0},e.prototype.reload=function(t){return this._load(t)},e.prototype._load=function(e){return"string"==typeof e?t.ajax(e,{dataType:"json"}).done(function(t){return function(e){return t.save(e)}}(this)):this.save(e)},e}();var c;c=function(){function e(e){this.context=e,this.$el=t("
          "),this.$elUl=this.$el.children(),this.timeoutID=null,this.context.$el.append(this.$el),this.bindEvent()}return e.prototype.init=function(){var t,e;return e=this.context.getOpt("alias")||this.context.at.charCodeAt(0),t=this.context.getOpt("headerTpl"),t&&1===this.$el.children().length&&this.$el.prepend(t),this.$el.attr({id:"at-view-"+e})},e.prototype.destroy=function(){return this.$el.remove()},e.prototype.bindEvent=function(){var e,i,n;return e=this.$el.find("ul"),i=0,n=0,e.on("mousemove.atwho-view","li",function(r){return function(r){var o;if((i!==r.clientX||n!==r.clientY)&&(i=r.clientX,n=r.clientY,o=t(r.currentTarget),!o.hasClass("cur")))return e.find(".cur").removeClass("cur"),o.addClass("cur")}}(this)).on("click.atwho-view","li",function(i){return function(n){return e.find(".cur").removeClass("cur"),t(n.currentTarget).addClass("cur"),i.choose(n),n.preventDefault()}}(this))},e.prototype.visible=function(){return t.expr.filters.visible(this.$el[0])},e.prototype.highlighted=function(){return this.$el.find(".cur").length>0},e.prototype.choose=function(t){var e,i;return(e=this.$el.find(".cur")).length&&(i=this.context.insertContentFor(e),this.context._stopDelayedCall(),this.context.insert(this.context.callbacks("beforeInsert").call(this.context,i,e,t),e),this.context.trigger("inserted",[e,t]),this.hide(t)),this.context.getOpt("hideWithoutSuffix")?this.stopShowing=!0:void 0},e.prototype.reposition=function(e){var i,n,r,o;return i=this.context.app.iframeAsRoot?this.context.app.window:window,e.bottom+this.$el.height()-t(i).scrollTop()>t(i).height()&&(e.bottom=e.top-this.$el.height()),e.left>(r=t(i).width()-this.$el.width()-5)&&(e.left=r),n={left:e.left,top:e.bottom},null!=(o=this.context.callbacks("beforeReposition"))&&o.call(this.context,n),this.$el.offset(n),this.context.trigger("reposition",[n])},e.prototype.next=function(){var t,e,i,n;return t=this.$el.find(".cur").removeClass("cur"),e=t.next(),e.length||(e=this.$el.find("li:first")),e.addClass("cur"),i=e[0],n=i.offsetTop+i.offsetHeight+(i.nextSibling?i.nextSibling.offsetHeight:0),this.scrollTop(Math.max(0,n-this.$el.height()))},e.prototype.prev=function(){var t,e,i,n;return t=this.$el.find(".cur").removeClass("cur"),i=t.prev(),i.length||(i=this.$el.find("li:last")),i.addClass("cur"),n=i[0],e=n.offsetTop+n.offsetHeight+(n.nextSibling?n.nextSibling.offsetHeight:0),this.scrollTop(Math.max(0,e-this.$el.height()))},e.prototype.scrollTop=function(t){var e;return e=this.context.getOpt("scrollDuration"),e?this.$elUl.animate({scrollTop:t},e):this.$elUl.scrollTop(t)},e.prototype.show=function(){var t;return this.stopShowing?void(this.stopShowing=!1):(this.visible()||(this.$el.show(),this.$el.scrollTop(0),this.context.trigger("shown")),(t=this.context.rect())?this.reposition(t):void 0)},e.prototype.hide=function(t,e){var i;if(this.visible())return isNaN(e)?(this.$el.hide(),this.context.trigger("hidden",[t])):(i=function(t){return function(){return t.hide()}}(this),clearTimeout(this.timeoutID),this.timeoutID=setTimeout(i,e))},e.prototype.render=function(e){var i,n,r,o,s,a,h;if(!(t.isArray(e)&&e.length>0))return void this.hide();for(this.$el.find("ul").empty(),n=this.$el.find("ul"),h=this.context.getOpt("displayTpl"),r=0,s=e.length;s>r;r++)o=e[r],o=t.extend({},o,{"atwho-at":this.context.at}),a=this.context.callbacks("tplEval").call(this.context,h,o,"onDisplay"),i=t(this.context.callbacks("highlighter").call(this.context,a,this.context.query.text)),i.data("item-data",o),n.append(i);return this.show(),this.context.getOpt("highlightFirst")?n.find("li:first").addClass("cur"):void 0},e}();var p;p={load:function(t,e){var i;return(i=this.controller(t))?i.model.load(e):void 0},isSelecting:function(){var t;return!!(null!=(t=this.controller())?t.view.visible():void 0)},hide:function(){var t;return null!=(t=this.controller())?t.view.hide():void 0},reposition:function(){var t;return(t=this.controller())?t.view.reposition(t.rect()):void 0},setIframe:function(t,e){return this.setupRootElement(t,e),null},run:function(){return this.dispatch()},destroy:function(){return this.shutdown(),this.$inputor.data("atwho",null)}},t.fn.atwho=function(e){var i,r;return i=arguments,r=null,this.filter('textarea, input, [contenteditable=""], [contenteditable=true]').each(function(){var o,s;return(s=(o=t(this)).data("atwho"))||o.data("atwho",s=new n(this)),"object"!=typeof e&&e?p[e]&&s?r=p[e].apply(s,Array.prototype.slice.call(i,1)):t.error("Method "+e+" does not exist on jQuery.atwho"):s.reg(e.at,e)}),null!=r?r:this},t.fn.atwho["default"]={at:void 0,alias:void 0,data:null,displayTpl:"
        • ${name}
        • ",insertTpl:"${atwho-at}${name}",headerTpl:null,callbacks:e,functionOverrides:{},searchKey:"name",suffix:void 0,hideWithoutSuffix:!1,startWithSpace:!0,acceptSpaceBar:!1,highlightFirst:!0,limit:5,maxLen:20,minLen:0,displayTimeout:300,delay:null,spaceSelectsMatch:!1,tabSelectsMatch:!0,editableAtwhoQueryAttrs:{},scrollDuration:150,suspendOnComposing:!0,lookUpOnClick:!0},t.fn.atwho.debug=!1}); \ No newline at end of file diff --git a/public/js/jquery.caret.js b/public/js/jquery.caret.js deleted file mode 100644 index 811ec63e..00000000 --- a/public/js/jquery.caret.js +++ /dev/null @@ -1,436 +0,0 @@ -(function (root, factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(["jquery"], function ($) { - return (root.returnExportsGlobal = factory($)); - }); - } else if (typeof exports === 'object') { - // Node. Does not work with strict CommonJS, but - // only CommonJS-like enviroments that support module.exports, - // like Node. - module.exports = factory(require("jquery")); - } else { - factory(jQuery); - } -}(this, function ($) { - -/* - 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 内的插入符 -只实现了获得插入符在文本框中的位置,我设置 -插入符的位置. -*/ - -"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) { - var fn, found, offset, sel; - if (sel = oWindow.getSelection()) { - offset = 0; - found = false; - (fn = function(pos, parent) { - var node, range, _i, _len, _ref, _results; - _ref = parent.childNodes; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - node = _ref[_i]; - if (found) { - break; - } - if (node.nodeType === 3) { - if (offset + node.length >= pos) { - found = true; - range = oDocument.createRange(); - range.setStart(node, pos - offset); - sel.removeAllRanges(); - sel.addRange(range); - break; - } else { - _results.push(offset += node.length); - } - } else { - _results.push(fn(pos, node)); - } - } - return _results; - })(pos, this.domInputor); - } - 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, shadowCaret; - if (oWindow.getSelection && (range = this.range())) { - if (range.endOffset - 1 > 0 && range.endContainer !== this.domInputor) { - 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(); - } - if (!offset || (offset != null ? offset.height : void 0) === 0) { - clonedRange = range.cloneRange(); - shadowCaret = $(oDocument.createTextNode("|")); - clonedRange.insertNode(shadowCaret[0]); - clonedRange.selectNode(shadowCaret[0]); - rect = clonedRange.getBoundingClientRect(); - offset = { - height: rect.height, - left: rect.left, - top: rect.top - }; - shadowCaret.remove(); - 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) { - value = value.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g, "
          "); - if (/firefox/i.test(navigator.userAgent)) { - value = value.replace(/\s/g, ' '); - } - return value; - }; - if (pos === void 0) { - pos = this.getPos(); - } - start_range = $inputor.val().slice(0, pos); - end_range = $inputor.val().slice(pos); - html = "" + format(start_range) + ""; - html += "|"; - html += "" + format(end_range) + ""; - 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 = $('
          '); - 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; - -$.fn.caret.apis = methods; - - -})); diff --git a/public/js/jquery.caret.min.js b/public/js/jquery.caret.min.js deleted file mode 100644 index a25584e2..00000000 --- a/public/js/jquery.caret.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jquery.caret 2016-02-27 */ -!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(c){return a.returnExportsGlobal=b(c)}):"object"==typeof exports?module.exports=b(require("jquery")):b(jQuery)}(this,function(a){"use strict";var b,c,d,e,f,g,h,i,j,k,l;k="caret",b=function(){function b(a){this.$inputor=a,this.domInputor=this.$inputor[0]}return b.prototype.setPos=function(a){var b,c,d,e;return(e=j.getSelection())&&(d=0,c=!1,(b=function(a,f){var g,i,j,k,l,m;for(l=f.childNodes,m=[],j=0,k=l.length;k>j&&(g=l[j],!c);j++)if(3===g.nodeType){if(d+g.length>=a){c=!0,i=h.createRange(),i.setStart(g,a-d),e.removeAllRanges(),e.addRange(i);break}m.push(d+=g.length)}else m.push(b(a,g));return m})(a,this.domInputor)),this.domInputor},b.prototype.getIEPosition=function(){return this.getPosition()},b.prototype.getPosition=function(){var a,b;return b=this.getOffset(),a=this.$inputor.offset(),b.left-=a.left,b.top-=a.top,b},b.prototype.getOldIEPos=function(){var a,b;return b=h.selection.createRange(),a=h.body.createTextRange(),a.moveToElementText(this.domInputor),a.setEndPoint("EndToEnd",b),a.text.length},b.prototype.getPos=function(){var a,b,c;return(c=this.range())?(a=c.cloneRange(),a.selectNodeContents(this.domInputor),a.setEnd(c.endContainer,c.endOffset),b=a.toString().length,a.detach(),b):h.selection?this.getOldIEPos():void 0},b.prototype.getOldIEOffset=function(){var a,b;return a=h.selection.createRange().duplicate(),a.moveStart("character",-1),b=a.getBoundingClientRect(),{height:b.bottom-b.top,left:b.left,top:b.top}},b.prototype.getOffset=function(){var b,c,d,e,f;return j.getSelection&&(d=this.range())?(d.endOffset-1>0&&d.endContainer!==this.domInputor&&(b=d.cloneRange(),b.setStart(d.endContainer,d.endOffset-1),b.setEnd(d.endContainer,d.endOffset),e=b.getBoundingClientRect(),c={height:e.height,left:e.left+e.width,top:e.top},b.detach()),c&&0!==(null!=c?c.height:void 0)||(b=d.cloneRange(),f=a(h.createTextNode("|")),b.insertNode(f[0]),b.selectNode(f[0]),e=b.getBoundingClientRect(),c={height:e.height,left:e.left,top:e.top},f.remove(),b.detach())):h.selection&&(c=this.getOldIEOffset()),c&&(c.top+=a(j).scrollTop(),c.left+=a(j).scrollLeft()),c},b.prototype.range=function(){var a;if(j.getSelection)return a=j.getSelection(),a.rangeCount>0?a.getRangeAt(0):null},b}(),c=function(){function b(a){this.$inputor=a,this.domInputor=this.$inputor[0]}return b.prototype.getIEPos=function(){var a,b,c,d,e,f,g;return b=this.domInputor,f=h.selection.createRange(),e=0,f&&f.parentElement()===b&&(d=b.value.replace(/\r\n/g,"\n"),c=d.length,g=b.createTextRange(),g.moveToBookmark(f.getBookmark()),a=b.createTextRange(),a.collapse(!1),e=g.compareEndPoints("StartToEnd",a)>-1?c:-g.moveStart("character",-c)),e},b.prototype.getPos=function(){return h.selection?this.getIEPos():this.domInputor.selectionStart},b.prototype.setPos=function(a){var b,c;return b=this.domInputor,h.selection?(c=b.createTextRange(),c.move("character",a),c.select()):b.setSelectionRange&&b.setSelectionRange(a,a),b},b.prototype.getIEOffset=function(a){var b,c,d,e;return c=this.domInputor.createTextRange(),a||(a=this.getPos()),c.move("character",a),d=c.boundingLeft,e=c.boundingTop,b=c.boundingHeight,{left:d,top:e,height:b}},b.prototype.getOffset=function(b){var c,d,e;return c=this.$inputor,h.selection?(d=this.getIEOffset(b),d.top+=a(j).scrollTop()+c.scrollTop(),d.left+=a(j).scrollLeft()+c.scrollLeft(),d):(d=c.offset(),e=this.getPosition(b),d={left:d.left+e.left-c.scrollLeft(),top:d.top+e.top-c.scrollTop(),height:e.height})},b.prototype.getPosition=function(a){var b,c,e,f,g,h,i;return b=this.$inputor,f=function(a){return a=a.replace(/<|>|`|"|&/g,"?").replace(/\r\n|\r|\n/g,"
          "),/firefox/i.test(navigator.userAgent)&&(a=a.replace(/\s/g," ")),a},void 0===a&&(a=this.getPos()),i=b.val().slice(0,a),e=b.val().slice(a),g=""+f(i)+"",g+="|",g+=""+f(e)+"",h=new d(b),c=h.create(g).rect()},b.prototype.getIEPosition=function(a){var b,c,d,e,f;return d=this.getIEOffset(a),c=this.$inputor.offset(),e=d.left-c.left,f=d.top-c.top,b=d.height,{left:e,top:f,height:b}},b}(),d=function(){function b(a){this.$inputor=a}return b.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"],b.prototype.mirrorCss=function(){var b,c=this;return b={position:"absolute",left:-9999,top:0,zIndex:-2e4},"TEXTAREA"===this.$inputor.prop("tagName")&&this.css_attr.push("width"),a.each(this.css_attr,function(a,d){return b[d]=c.$inputor.css(d)}),b},b.prototype.create=function(b){return this.$mirror=a("
          "),this.$mirror.css(this.mirrorCss()),this.$mirror.html(b),this.$inputor.after(this.$mirror),this},b.prototype.rect=function(){var a,b,c;return a=this.$mirror.find("#caret"),b=a.position(),c={left:b.left,top:b.top,height:a.height()},this.$mirror.remove(),c},b}(),e={contentEditable:function(a){return!(!a[0].contentEditable||"true"!==a[0].contentEditable)}},g={pos:function(a){return a||0===a?this.setPos(a):this.getPos()},position:function(a){return h.selection?this.getIEPosition(a):this.getPosition(a)},offset:function(a){var b;return b=this.getOffset(a)}},h=null,j=null,i=null,l=function(a){var b;return(b=null!=a?a.iframe:void 0)?(i=b,j=b.contentWindow,h=b.contentDocument||j.document):(i=void 0,j=window,h=document)},f=function(a){var b;h=a[0].ownerDocument,j=h.defaultView||h.parentWindow;try{return i=j.frameElement}catch(c){b=c}},a.fn.caret=function(d,f,h){var i;return g[d]?(a.isPlainObject(f)?(l(f),f=void 0):l(h),i=e.contentEditable(this)?new b(this):new c(this),g[d].apply(i,[f])):a.error("Method "+d+" does not exist on jQuery.caret")},a.fn.caret.EditableCaret=b,a.fn.caret.InputCaret=c,a.fn.caret.Utils=e,a.fn.caret.apis=g}); \ No newline at end of file From ec133a688001f2fa456ff80fb788f10bbbb87c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Sat, 23 Oct 2021 19:50:17 +0800 Subject: [PATCH 16/28] =?UTF-8?q?=E6=B7=BB=E5=8A=A0todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/tpm/challengesnew/tpm-md-editor.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index 437d2bce..85ee2219 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -87,6 +87,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const editorBodyId = `mdEditors_${mdID}`; const tipId = `e_tips_mdEditor_${mdID}`; + //todo:需要替换成可@用户列表接口,由于forge接口名称冲突,src\forge\UsersList\fork_users.js接口/members.json需要换成/forks.json useEffect(()=>{ isCanAtme && axios.get('/users/list.json',{ params: { @@ -145,7 +146,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //markdown编辑器中输入的键盘监听事件 function mdKeyDown(){ document.onkeydown = e=>{ - console.log("markdown",atWhoVisible); if (e.key === "@") { // 输入@键后在对应的位置显示可选的项目成员 setAtWhoVisible(true); @@ -195,7 +195,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla function atWhoKeyDown(){ //监听上下和enter键 document.onkeydown = (e) =>{ - console.log("atwho列表",atWhoVisible); const atWhoListDiv = document.getElementById("at_who_list"); const atWhoDivs = document.getElementsByClassName("at_who"); let index; From 2078a044b6321ca4889d77ca8d7064f42eb2856c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Mon, 1 Nov 2021 09:31:08 +0800 Subject: [PATCH 17/28] =?UTF-8?q?at=E6=88=91=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forge/Merge/Files.jsx | 2 +- .../tpm/challengesnew/tpm-md-editor.js | 106 ++++++++++++++---- 2 files changed, 87 insertions(+), 21 deletions(-) diff --git a/src/forge/Merge/Files.jsx b/src/forge/Merge/Files.jsx index b4cf5b48..6661c1d3 100644 --- a/src/forge/Merge/Files.jsx +++ b/src/forge/Merge/Files.jsx @@ -18,7 +18,7 @@ function Files({ data,history,owner,projectsId , parentsSha }){ useEffect(()=>{ document.addEventListener('click',()=>{setIsOpen(false)}) - }) + },[]) function showDown(flag,index,isBin){ if(!isBin){ diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index 85ee2219..9b50415d 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -136,6 +136,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla }) atWhoLoginList.current = Array.from(list); setAtWhoLoginListState(Array.from(list)); + //销毁atWhoKeyDown键盘监听事件 + // document.removeEventListener("keydown",atWhoKeyDown); } function onMouseOver(key){ @@ -144,9 +146,11 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla } //markdown编辑器中输入的键盘监听事件 - function mdKeyDown(){ - document.onkeydown = e=>{ - if (e.key === "@") { + function mdKeyDown(e){ + console.log("11111111111"); + // document.onkeydown = e=>{ + if (e.shiftKey && e.key === "@") { + console.log("markdown编辑器的鼠标事件",e); // 输入@键后在对应的位置显示可选的项目成员 setAtWhoVisible(true); //获取光标位置 @@ -161,7 +165,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla setAtWhoVisible(false); } //处理本来@了某人 -> 删掉 -> 撤回 的情况 - if(e.code === "KeyZ" && users.length != 0){ + if(e.ctrlKey && e.code === "KeyZ" && users.length != 0){ const codemirror = editorInstance.cm; let value = codemirror.getValue(); //处理初始内容就自带@谁的情况 @@ -188,30 +192,85 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla } }) } - } + // const atWhoListDiv = document.getElementById("at_who_list"); + // if(atWhoListDiv && (e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "Enter")){ + // return false; + // } + // const atWhoDivs = document.getElementsByClassName("at_who"); + // let index; + // for(let i = 0; i 0){ + // e.preventDefault(); + // index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40) + // atWhoDivs[index].className = "at_who"; + // atWhoDivs[index-1].className = "at_who active"; + // } + // if(e.key === "ArrowDown"){ + // e.preventDefault(); + // console.log("ArrowDown",atWhoVisible); + // const atWhoListDiv = document.getElementById("at_who_list"); + // const atWhoDivs = document.getElementsByClassName("at_who"); + // let index; + // for(let i = 0; i=3 && (atWhoListDiv.scrollTop +=40) + // atWhoDivs[index].className = "at_who"; + // atWhoDivs[index+1].className = "at_who active"; + // } + // if(e.key === "Enter"){ + // //阻止默认事件 + // e.preventDefault(); + // //找到classname为at_who active的div,执行click事件 + // document.getElementsByClassName("at_who active")[0].click(); + // } + + // editorInstance.addKeyMap({ + // 'ArrowUp':()=>{ + // index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40) + // atWhoDivs[index].className = "at_who"; + // atWhoDivs[index-1].className = "at_who active"; + // }, + // 'ArrowDown':()=>{ + // index >=3 && (atWhoListDiv.scrollTop +=40) + // atWhoDivs[index].className = "at_who"; + // atWhoDivs[index+1].className = "at_who active"; + // }, + // 'Enter':()=>{ + // //找到classname为at_who active的div,执行click事件 + // document.getElementsByClassName("at_who active")[0].click(); + // }, + // }) + + // } + // } } //弹出可选@用户列表之后的键盘监听事件 - function atWhoKeyDown(){ + function atWhoKeyDown(e){ //监听上下和enter键 - document.onkeydown = (e) =>{ + // document.onkeydown = (e) =>{ const atWhoListDiv = document.getElementById("at_who_list"); const atWhoDivs = document.getElementsByClassName("at_who"); let index; for(let i = 0; i 0){ - // index >=4 && (atWhoListDiv.scrollTop -=40) - atWhoListDiv.scrollTop -= 40; + index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40) atWhoDivs[index].className = "at_who"; atWhoDivs[index-1].className = "at_who active"; + return; } if(e.key === "ArrowDown" && index < atWhoDivs.length-1){ - // index >=3 && (atWhoListDiv.scrollTop +=40) - atWhoListDiv.scrollTop += 40; + index >=3 && (atWhoListDiv.scrollTop +=40) atWhoDivs[index].className = "at_who"; atWhoDivs[index+1].className = "at_who active"; + return; } if(e.key === "Enter"){ //阻止默认事件 @@ -219,9 +278,14 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //找到classname为at_who active的div,执行click事件 document.getElementsByClassName("at_who active")[0].click(); } - } + // } } + //点击其他地方关闭弹框 + useEffect(()=>{ + document.addEventListener('click',()=>{setAtWhoVisible(false)}) + },[]) + useEffect(()=>{ console.log('@谁列表发生变化,atWhoLoginList.current:',atWhoLoginList.current,'atWhoLoginListState: ',atWhoLoginListState); changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState); @@ -240,15 +304,17 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
          ) - useEffect(()=>{ - document.addEventListener('click',()=>{setAtWhoVisible(false)}); - }) - useEffect(()=>{ //当atWhoVisible为true的时候,失焦,监听上下和enter键 if(atWhoVisible){ - document.activeElement.id !== "blur_atWho" && document.getElementById("blur_atWho").focus(); - document.addEventListener("keydown",atWhoKeyDown()); + // document.activeElement.id !== "blur_atWho" && document.getElementById("blur_atWho").focus(); + // const atWhoDivs = document.getElementsByClassName("at_who"); + // let index = 0; + // for(let i = 0; i { - document.addEventListener("keydown", mdKeyDown()); + document.addEventListener("keydown", mdKeyDown); }); isCanAtme && editorInstance.cm.on("blur", () => { - document.removeEventListener("keydown",mdKeyDown()); + document.removeEventListener("keydown",mdKeyDown); }); editorInstance.cm.on("change", (cm) => { onChange && onChange(cm.getValue()); From ac4488a90711a42c1841627c3a9f7b8a6c2e8f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Mon, 8 Nov 2021 15:14:04 +0800 Subject: [PATCH 18/28] atwho --- src/forge/Merge/merge_form.js | 2 + src/forge/Order/order_form.js | 2 + src/forge/UsersList/fork_users.js | 2 +- src/forge/comments/comments.js | 5 ++- .../tpm/challengesnew/css/newquestion.css | 5 +++ .../tpm/challengesnew/tpm-md-editor.js | 37 ++++++++++++------- 6 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/forge/Merge/merge_form.js b/src/forge/Merge/merge_form.js index 141e585d..d050116f 100644 --- a/src/forge/Merge/merge_form.js +++ b/src/forge/Merge/merge_form.js @@ -285,6 +285,8 @@ class MergeForm extends Component { onChange={this.onContentChange} isCanAtme = {true} changeAtWhoLoginList = {this.changeAtWhoLoginList} + owner = {owner} + projectsId = {projectsId} >

          {get_attachments && get_attachments.length > 0 ? ( diff --git a/src/forge/UsersList/fork_users.js b/src/forge/UsersList/fork_users.js index 23cd5bea..a3d9c2a2 100644 --- a/src/forge/UsersList/fork_users.js +++ b/src/forge/UsersList/fork_users.js @@ -27,7 +27,7 @@ class ForkUsers extends Component { }); const { projectsId , owner } = this.props.match.params; - const url = `/${owner}/${projectsId}/members.json`; + const url = `/${owner}/${projectsId}/forks.json`; axios .get(url, { params: { diff --git a/src/forge/comments/comments.js b/src/forge/comments/comments.js index e070d549..8e514e16 100644 --- a/src/forge/comments/comments.js +++ b/src/forge/comments/comments.js @@ -65,7 +65,7 @@ class comments extends Component { atWhoLoginList, } = this.state; - const { owner } = this.props.match.params; + const url = `/issues/${orderId}/journals.json`; axios @@ -320,6 +320,7 @@ class comments extends Component { new_journal_id, } = this.state; const { current_user, only_show_content } = this.props; + const { projectsId ,owner } = this.props.match.params; const new_comment = (is_reply, item_id) => { return ( @@ -353,6 +354,8 @@ class comments extends Component { } isCanAtme = {true} changeAtWhoLoginList = {this.changeAtWhoLoginList} + owner = {owner} + projectsId = {projectsId} >

          {quillFlag && 请输入评论内容} diff --git a/src/modules/tpm/challengesnew/css/newquestion.css b/src/modules/tpm/challengesnew/css/newquestion.css index c6359f52..81d7afcb 100644 --- a/src/modules/tpm/challengesnew/css/newquestion.css +++ b/src/modules/tpm/challengesnew/css/newquestion.css @@ -35,6 +35,11 @@ border-radius:50%; margin-right: 10px; } +.at_who span{ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} .blur_atWho{ position: absolute; top: -100px; diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index 9b50415d..ae9f10e2 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -74,7 +74,7 @@ function md_elocalStorage(editor, mdu, id) { return tid } -export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true , isCanAtme = false ,changeAtWhoLoginList }) => { +export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true , isCanAtme = false , changeAtWhoLoginList, owner, projectsId }) => { const editorEl = useRef(); const resizeBarEl = useRef(); @@ -82,17 +82,18 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const [atWhoVisible, setAtWhoVisible] = useState(false); const [atWhoLoginListState, setAtWhoLoginListState] = useState([]); const [users, setUsers] = useState([]); + //用户输入的@之后的搜索词 + const [search, setSeacrch] = useState(undefined); const atWhoLoginList = useRef([]); const containerId = `mdEditor_${mdID}`; const editorBodyId = `mdEditors_${mdID}`; const tipId = `e_tips_mdEditor_${mdID}`; - //todo:需要替换成可@用户列表接口,由于forge接口名称冲突,src\forge\UsersList\fork_users.js接口/members.json需要换成/forks.json useEffect(()=>{ - isCanAtme && axios.get('/users/list.json',{ - params: { - search: 'admin', - }, + isCanAtme && axios.get(`/${owner}/${projectsId}/members.json`,{ + // params: { + // search: 'admin', + // }, }).then(response=>{ if(response && response.status === 200){ setUsers(response.data.users); @@ -147,10 +148,13 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //markdown编辑器中输入的键盘监听事件 function mdKeyDown(e){ - console.log("11111111111"); + const cm = editorInstance.cm; + //获取鼠标所在行的行数和ch + const line = cm.doc.getCursor().line;//行 + atWhoVisible && console.log('cmaa',cm.getLine(line)); + console.log("markdown编辑器--------键盘监听事件"); // document.onkeydown = e=>{ if (e.shiftKey && e.key === "@") { - console.log("markdown编辑器的鼠标事件",e); // 输入@键后在对应的位置显示可选的项目成员 setAtWhoVisible(true); //获取光标位置 @@ -161,8 +165,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //将第一个用户默认选中 const at_who_divs = document.getElementsByClassName("at_who"); at_who_divs[0].className = "at_who active"; - } else { - setAtWhoVisible(false); } //处理本来@了某人 -> 删掉 -> 撤回 的情况 if(e.ctrlKey && e.code === "KeyZ" && users.length != 0){ @@ -287,7 +289,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla },[]) useEffect(()=>{ - console.log('@谁列表发生变化,atWhoLoginList.current:',atWhoLoginList.current,'atWhoLoginListState: ',atWhoLoginListState); + console.log('@谁列表发生变化,atWhoLoginList.current:',atWhoLoginList.current); changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState); },[atWhoLoginListState]) @@ -307,6 +309,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla useEffect(()=>{ //当atWhoVisible为true的时候,失焦,监听上下和enter键 if(atWhoVisible){ + const cm = editorInstance.cm; + //获取鼠标所在行的行数和ch + const line = cm.doc.getCursor().line;//行 + // console.log('cmaa',cm.getLine(line)); // document.activeElement.id !== "blur_atWho" && document.getElementById("blur_atWho").focus(); // const atWhoDivs = document.getElementsByClassName("at_who"); // let index = 0; @@ -314,7 +320,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla // atWhoDivs[i].className === "at_who active" && (index = i); // } // const atWhoListDiv = document.getElementById("at_who_list"); - document.addEventListener("keydown",atWhoKeyDown); + // document.addEventListener("keydown",atWhoKeyDown); } },[atWhoVisible]) @@ -416,7 +422,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla tid = md_elocalStorage(editorInstance, `MDEditor__${containerId}`, containerId) } //isCanAtme:只有issue和合并请求以及评论部分可以@他人操作 - //当光标或选中内容时触发绑定@事件 + //绑定@事件 isCanAtme && editorInstance.cm.on("focus", () => { document.addEventListener("keydown", mdKeyDown); }); @@ -425,6 +431,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla }); editorInstance.cm.on("change", (cm) => { onChange && onChange(cm.getValue()); + //当内容发生改变并且有已@列表时 if(atWhoLoginList.current.length != 0){ const codemirror = editorInstance.cm; let value = codemirror.getValue(); @@ -480,6 +487,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla }) } } + // const cm = editorInstance.cm; + //获取鼠标所在行的行数和ch + const line = cmEl.doc.getCursor().line;//行 + atWhoVisible && console.log('cmaa',cmEl.getLine(line)); }); ro = onLayout() return () => { From 55a6f93fe3a9f0fc657b9b23afa4e750d0083cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Mon, 8 Nov 2021 16:04:19 +0800 Subject: [PATCH 19/28] atwho --- src/modules/tpm/challengesnew/tpm-md-editor.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index ae9f10e2..385005b6 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -126,7 +126,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const line = cursor.line;//行 const ch = cursor.ch;//列 //替换最后的内容 - // cm.getLine(line).endsWith("@") ? cm.replaceRange(username+" ",{line,ch},{line,ch}) : cm.replaceRange("@"+username+" ",{line,ch},{line,ch}) cm.replaceRange(username+" ",{line,ch},{line,ch}); //鼠标聚焦 cm.focus(); @@ -148,10 +147,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //markdown编辑器中输入的键盘监听事件 function mdKeyDown(e){ - const cm = editorInstance.cm; - //获取鼠标所在行的行数和ch - const line = cm.doc.getCursor().line;//行 - atWhoVisible && console.log('cmaa',cm.getLine(line)); console.log("markdown编辑器--------键盘监听事件"); // document.onkeydown = e=>{ if (e.shiftKey && e.key === "@") { @@ -308,11 +303,12 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla useEffect(()=>{ //当atWhoVisible为true的时候,失焦,监听上下和enter键 + // atWhoVisible && editorInstance.cm. if(atWhoVisible){ const cm = editorInstance.cm; //获取鼠标所在行的行数和ch const line = cm.doc.getCursor().line;//行 - // console.log('cmaa',cm.getLine(line)); + console.log('useEffect',cm.getLine(line)); // document.activeElement.id !== "blur_atWho" && document.getElementById("blur_atWho").focus(); // const atWhoDivs = document.getElementsByClassName("at_who"); // let index = 0; @@ -487,10 +483,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla }) } } - // const cm = editorInstance.cm; - //获取鼠标所在行的行数和ch - const line = cmEl.doc.getCursor().line;//行 - atWhoVisible && console.log('cmaa',cmEl.getLine(line)); }); ro = onLayout() return () => { From f003a997797af5ec2cf63f74cdd6499416483227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Wed, 10 Nov 2021 16:47:22 +0800 Subject: [PATCH 20/28] =?UTF-8?q?@=E6=88=91=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forge/Head/NoticeContent.jsx | 2 +- .../SecuritySetting/notice/myNotice/Index.jsx | 46 ++- .../tpm/challengesnew/css/newquestion.css | 6 +- .../tpm/challengesnew/tpm-md-editor.js | 325 ++++++++---------- 4 files changed, 197 insertions(+), 182 deletions(-) diff --git a/src/forge/Head/NoticeContent.jsx b/src/forge/Head/NoticeContent.jsx index 634a7adc..39546707 100644 --- a/src/forge/Head/NoticeContent.jsx +++ b/src/forge/Head/NoticeContent.jsx @@ -224,7 +224,7 @@ function NoticeContent({ visible, showNotification, resetUserInfo, current_user:

          - " + (item.sender ? item.sender.name : '') + "   " + item.content + " 中@我" }}> + {item.time_ago}
          diff --git a/src/forge/SecuritySetting/notice/myNotice/Index.jsx b/src/forge/SecuritySetting/notice/myNotice/Index.jsx index 22660691..3d495722 100644 --- a/src/forge/SecuritySetting/notice/myNotice/Index.jsx +++ b/src/forge/SecuritySetting/notice/myNotice/Index.jsx @@ -17,7 +17,7 @@ function MyNotice(props) { const [selectedNum, setSelectedNum] = useState(0);//@我批量删除选择消息条数 const [isBatchDelete, setIsBatchDelete] = useState(false);//@我是否批量删除 const [batchDeleteCheckedAll, setBatchDeleteCheckAll] = useState(false);//@我批量删除--全选 - + const [messageType, setMessageType] = useState(undefined); const [noticeUnreadCount, setNoticeUnreadCount] = useState();//未读系统通知数量 // const [letterUnreadCount, setLetterUnreadCount] = useState(0);//未读私信数量 const [atUnreadCount, setAtUnreadCount] = useState();//未读@我数量 @@ -65,6 +65,7 @@ function MyNotice(props) { setAtUnreadCount(response.data.unread_atme); setMessageList(response.data.messages); setMessTotalCount(response.data.total_count); + setMessageType(response.data.type); } }); } @@ -203,7 +204,46 @@ function MyNotice(props) {
          } - {messageList && messageList.map(item => { + {/* 系统消息 */} + {messageType === "notification" && messageList && messageList.map(item =>{ + return ( +
          +
          + {item.status === 1 ? : } + + {turnToMess(item)}} dangerouslySetInnerHTML={{__html: item.content}}> +
          +
          + {item.time_ago} + {item.status === 1 && readNotice([item.id])}>标记为已读} +
          +
          + ) + })} + + {/* @我消息 */} + {messageType === "atme" && messageList && messageList.map(item =>{ + return ( +
          +
          + + {item.sender && {window.open(`/${item.sender && item.sender.login}`);}}/>} +
          {turnToMess(item)}}> + {item.status === 1 ? : } + {item.sender && } +
          +
          +
          + {item.time_ago} + {!isBatchDelete && item.status === 1 && readNotice([item.id])}>标记为已读}    + {!isBatchDelete && deleteNotice([item.id])}>删除} +
          +
          + ) + })} + + {false && messageList && messageList.map(item => { + console.log('item',item); // 系统消息 if (noticeType === "0") { // 消息类别 @@ -229,7 +269,7 @@ function MyNotice(props) { {item.sender && {window.open(`/${item.sender && item.sender.login}`);}}/>}
          {turnToMess(item)}}> {item.status === 1 ? : } - {item.sender && " + item.sender.name+ " "+ item.content +" 中@我"}}>} + {item.sender && " + item.sender.name+ " "+ item.content}}>}
          diff --git a/src/modules/tpm/challengesnew/css/newquestion.css b/src/modules/tpm/challengesnew/css/newquestion.css index 81d7afcb..eb1a67eb 100644 --- a/src/modules/tpm/challengesnew/css/newquestion.css +++ b/src/modules/tpm/challengesnew/css/newquestion.css @@ -26,7 +26,7 @@ border-bottom: 1px solid rgba(212, 212, 212, 0.5); padding: 0 4px; } -.active{ +.at_who.active{ background: #F3F4F6; } .at_who img{ @@ -39,8 +39,4 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; -} -.blur_atWho{ - position: absolute; - top: -100px; } \ No newline at end of file diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index 385005b6..988e9381 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -75,30 +75,35 @@ function md_elocalStorage(editor, mdu, id) { } export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true , isCanAtme = false , changeAtWhoLoginList, owner, projectsId }) => { - + const editorEl = useRef(); const resizeBarEl = useRef(); const [editorInstance, setEditorInstance] = useState(); const [atWhoVisible, setAtWhoVisible] = useState(false); const [atWhoLoginListState, setAtWhoLoginListState] = useState([]); + //调用member.json接口获取到的用户列表 const [users, setUsers] = useState([]); - //用户输入的@之后的搜索词 - const [search, setSeacrch] = useState(undefined); + //可以@的全部用户 + const [allUsers, setAllUsers] = useState([]); const atWhoLoginList = useRef([]); + const atWhoVisibleRef = useRef(false); const containerId = `mdEditor_${mdID}`; const editorBodyId = `mdEditors_${mdID}`; const tipId = `e_tips_mdEditor_${mdID}`; useEffect(()=>{ - isCanAtme && axios.get(`/${owner}/${projectsId}/members.json`,{ - // params: { - // search: 'admin', - // }, - }).then(response=>{ - if(response && response.status === 200){ + //请求members接口获取全部可@列表 + isCanAtme && axios.get(`/${owner}/${projectsId}/members.json`).then(response=>{ + if(response.data.total_count !== 0){ + setAllUsers(response.data.users); setUsers(response.data.users); } }) + //点击其他地方关闭弹框 + document.addEventListener('click',()=>{ + atWhoVisibleRef.current = false; + setAtWhoVisible(false); + }) },[]) function onLayout() { @@ -119,14 +124,16 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla } function selectAtWho(username){ + atWhoVisibleRef.current = false; setAtWhoVisible(false); const cm = editorInstance.cm; //获取鼠标所在行的行数和ch const cursor = cm.doc.getCursor(); const line = cursor.line;//行 const ch = cursor.ch;//列 + const startIndex = cm.getRange({line,ch:0},{line,ch}).lastIndexOf("@")+1; //替换最后的内容 - cm.replaceRange(username+" ",{line,ch},{line,ch}); + cm.replaceRange(username+" ",{line,ch:startIndex},{line,ch}); //鼠标聚焦 cm.focus(); //将此user的login存储到atWhoLoginList集合中 @@ -136,155 +143,58 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla }) atWhoLoginList.current = Array.from(list); setAtWhoLoginListState(Array.from(list)); - //销毁atWhoKeyDown键盘监听事件 - // document.removeEventListener("keydown",atWhoKeyDown); } function onMouseOver(key){ - document.getElementsByClassName("at_who active")[0].className="at_who"; - document.getElementsByClassName("at_who")[key].className="at_who active"; + document.getElementsByClassName("at_who active")[0] && (document.getElementsByClassName("at_who active")[0].className="at_who"); + document.getElementsByClassName("at_who")[key] && (document.getElementsByClassName("at_who")[key].className="at_who active"); } //markdown编辑器中输入的键盘监听事件 function mdKeyDown(e){ - console.log("markdown编辑器--------键盘监听事件"); - // document.onkeydown = e=>{ - if (e.shiftKey && e.key === "@") { - // 输入@键后在对应的位置显示可选的项目成员 - setAtWhoVisible(true); - //获取光标位置 - const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style; - //设置弹框位置 - document.getElementById("at_who_list").style.top = (parseInt(cssStyle.getPropertyValue("top").replace("px",""))+60)+"px"; - document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+20)+"px"; - //将第一个用户默认选中 - const at_who_divs = document.getElementsByClassName("at_who"); - at_who_divs[0].className = "at_who active"; - } - //处理本来@了某人 -> 删掉 -> 撤回 的情况 - if(e.ctrlKey && e.code === "KeyZ" && users.length != 0){ - const codemirror = editorInstance.cm; - let value = codemirror.getValue(); - //处理初始内容就自带@谁的情况 - if(initValue){ - const del = []; - users.map(item=>{ - if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ - //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 - del[del.length] = `@${item.username}`; - } - }) - del.length!=0 && del.map(str=>{ - value = value.replace(str,""); - }) - } - //判断value是否包含@符号 - value.indexOf("@") != -1 && users.map(item =>{ - if(value.indexOf(item.username)!=-1 && value.charAt(value.indexOf(item.username)-1) ==="@"){ - //将此user的login存储到atWhoLoginList集合中 - const list = new Set(atWhoLoginList.current); - list.add(item.login); - atWhoLoginList.current = Array.from(list); - setAtWhoLoginListState(Array.from(list)); + if (e.shiftKey && e.code === "Digit2") { + // 输入@键后在对应的位置显示可选的项目成员 + atWhoVisibleRef.current = true; + setAtWhoVisible(true); + //获取光标位置 + const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style; + //设置弹框位置 + const newTop = placeholder === "添加评论..." ? 159: placeholder === "请输入合并请求的描述..." ? 172:62; + const newLeft = placeholder === "添加评论..." ? 80: 20; + document.getElementById("at_who_list").style.top = parseInt(cssStyle.getPropertyValue("top").replace("px","")) + newTop +"px"; + document.getElementById("at_who_list").style.left = parseInt(cssStyle.getPropertyValue("left").replace("px",""))+newLeft+"px"; + } + //处理本来@了某人 -> 删掉 -> 撤回 的情况 + if(e.ctrlKey && e.code === "KeyZ" && allUsers.length != 0){ + const codemirror = editorInstance.cm; + let value = codemirror.getValue(); + //处理初始内容就自带@谁的情况 + if(initValue){ + const del = []; + allUsers.map(item=>{ + if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ + //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 + del[del.length] = `@${item.username}`; } }) + del.length!=0 && del.map(str=>{ + value = value.replace(str,""); + }) } - // const atWhoListDiv = document.getElementById("at_who_list"); - // if(atWhoListDiv && (e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "Enter")){ - // return false; - // } - // const atWhoDivs = document.getElementsByClassName("at_who"); - // let index; - // for(let i = 0; i 0){ - // e.preventDefault(); - // index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40) - // atWhoDivs[index].className = "at_who"; - // atWhoDivs[index-1].className = "at_who active"; - // } - // if(e.key === "ArrowDown"){ - // e.preventDefault(); - // console.log("ArrowDown",atWhoVisible); - // const atWhoListDiv = document.getElementById("at_who_list"); - // const atWhoDivs = document.getElementsByClassName("at_who"); - // let index; - // for(let i = 0; i=3 && (atWhoListDiv.scrollTop +=40) - // atWhoDivs[index].className = "at_who"; - // atWhoDivs[index+1].className = "at_who active"; - // } - // if(e.key === "Enter"){ - // //阻止默认事件 - // e.preventDefault(); - // //找到classname为at_who active的div,执行click事件 - // document.getElementsByClassName("at_who active")[0].click(); - // } - - // editorInstance.addKeyMap({ - // 'ArrowUp':()=>{ - // index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40) - // atWhoDivs[index].className = "at_who"; - // atWhoDivs[index-1].className = "at_who active"; - // }, - // 'ArrowDown':()=>{ - // index >=3 && (atWhoListDiv.scrollTop +=40) - // atWhoDivs[index].className = "at_who"; - // atWhoDivs[index+1].className = "at_who active"; - // }, - // 'Enter':()=>{ - // //找到classname为at_who active的div,执行click事件 - // document.getElementsByClassName("at_who active")[0].click(); - // }, - // }) - - // } - // } + //判断value是否包含@符号 + value.indexOf("@") != -1 && allUsers.map(item =>{ + if(value.indexOf(item.username)!=-1 && value.charAt(value.indexOf(item.username)-1) ==="@"){ + //将此user的login存储到atWhoLoginList集合中 + const list = new Set(atWhoLoginList.current); + list.add(item.login); + atWhoLoginList.current = Array.from(list); + setAtWhoLoginListState(Array.from(list)); + } + }) + } } - //弹出可选@用户列表之后的键盘监听事件 - function atWhoKeyDown(e){ - //监听上下和enter键 - // document.onkeydown = (e) =>{ - const atWhoListDiv = document.getElementById("at_who_list"); - const atWhoDivs = document.getElementsByClassName("at_who"); - let index; - for(let i = 0; i 0){ - index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40) - atWhoDivs[index].className = "at_who"; - atWhoDivs[index-1].className = "at_who active"; - return; - } - if(e.key === "ArrowDown" && index < atWhoDivs.length-1){ - index >=3 && (atWhoListDiv.scrollTop +=40) - atWhoDivs[index].className = "at_who"; - atWhoDivs[index+1].className = "at_who active"; - return; - } - if(e.key === "Enter"){ - //阻止默认事件 - e.preventDefault(); - //找到classname为at_who active的div,执行click事件 - document.getElementsByClassName("at_who active")[0].click(); - } - // } - } - - //点击其他地方关闭弹框 useEffect(()=>{ - document.addEventListener('click',()=>{setAtWhoVisible(false)}) - },[]) - - useEffect(()=>{ - console.log('@谁列表发生变化,atWhoLoginList.current:',atWhoLoginList.current); changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState); },[atWhoLoginListState]) @@ -292,8 +202,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
          {users && users.map((item,key)=>{ return( -
          {selectAtWho(item.username)}} onMouseOver={()=>{onMouseOver(key)}}> - {item.image_url && } +
          {selectAtWho(item.username)}} onMouseOver={()=>{onMouseOver(key)}}> + {item.image_url && } {item.username}
          ) @@ -301,25 +211,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
          ) - useEffect(()=>{ - //当atWhoVisible为true的时候,失焦,监听上下和enter键 - // atWhoVisible && editorInstance.cm. - if(atWhoVisible){ - const cm = editorInstance.cm; - //获取鼠标所在行的行数和ch - const line = cm.doc.getCursor().line;//行 - console.log('useEffect',cm.getLine(line)); - // document.activeElement.id !== "blur_atWho" && document.getElementById("blur_atWho").focus(); - // const atWhoDivs = document.getElementsByClassName("at_who"); - // let index = 0; - // for(let i = 0; i { if (editorInstance) { return @@ -402,6 +293,69 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const cmEl = editorInstance && editorInstance.cm + useEffect(()=>{ + if(atWhoVisibleRef.current){ + // 添加上下键、enter键监听事件 + cmEl.addKeyMap({ + 'Up':()=>{ + const atWhoListDiv = document.getElementById("at_who_list"); + const atWhoDivs = document.getElementsByClassName("at_who"); + let index; + for(let i = 0; i0){ + index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40) + atWhoDivs[index].className = "at_who"; + atWhoDivs[index-1].className = "at_who active"; + } + }, + 'Down':()=>{ + const atWhoListDiv = document.getElementById("at_who_list"); + const atWhoDivs = document.getElementsByClassName("at_who"); + let index; + for(let i = 0; i=3 && (atWhoListDiv.scrollTop +=40) + atWhoDivs[index].className = "at_who"; + atWhoDivs[index+1].className = "at_who active"; + } + }, + 'Enter':()=>{ + //找到classname为at_who active的div,执行click事件 + if(document.getElementsByClassName("at_who active")[0]){ + document.getElementsByClassName("at_who active")[0].click() + }else{ + const cm = editorInstance.cm; + const cursor = cm.doc.getCursor(); + const line = cursor.line;//行 + const ch = cursor.ch;//列 + //添加换行 + cm.replaceRange("\n",{line,ch},{line,ch}); + setAtWhoVisible(false); + atWhoVisibleRef.current = false; + } + } + }) + } else { + //移除上下、enter键监听 + cmEl && cmEl.removeKeyMap(); + } + },[atWhoVisible]) + + useEffect(()=>{ + //当users数组发生变化时改变框的位置 + if(atWhoVisibleRef.current && users){ + //获取光标位置 + const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style; + //设置弹框位置 + const newLeft = placeholder === "添加评论..."? 80: 10; + document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+newLeft)+"px"; + } + },[users]) + useEffect(() => { if (cmEl) { let tid = null @@ -426,7 +380,34 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla document.removeEventListener("keydown",mdKeyDown); }); editorInstance.cm.on("change", (cm) => { + //调用父组件的onchange方法,将输入内容传入父级组件 onChange && onChange(cm.getValue()); + if(atWhoVisibleRef.current){ + //搜索用户(弹框之后用户输入用户名信息) + const cur = cm.doc.getCursor(); + const line = cur.line; + const ch = cur.ch; + let rangeCont = cmEl.getRange({line,ch:0},{line,ch}); + //处理已经弹出列表框,但用户删除@符号 + if(rangeCont.indexOf("@")===-1){ + setAtWhoVisible(false); + atWhoVisibleRef.current = false; + }else{ + rangeCont = rangeCont.substring(rangeCont.lastIndexOf("@")+1); + rangeCont ? axios.get(`/${owner}/${projectsId}/members.json`,{ + params: { + search: rangeCont, + }, + }).then(response=>{ + if(response && response.data && response.data.total_count !== 0){ + setUsers(response.data.users); + }else{ + setUsers(undefined); + } + }):setUsers(allUsers) + } + } + //当内容发生改变并且有已@列表时 if(atWhoLoginList.current.length != 0){ const codemirror = editorInstance.cm; @@ -434,7 +415,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //处理初始内容就自带@谁的情况 if(initValue){ const del = []; - users.map(item=>{ + allUsers.map(item=>{ if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 del[del.length] = `@${item.username}`; @@ -447,7 +428,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //以username为主键,login为value的map集合 let atWhoMap = new Map(); Array.from(atWhoLoginList.current).map(item=>{ - users.map(i=>{ + allUsers.map(i=>{ if(i.login === item){ atWhoMap.set(i.username,i.login); } @@ -471,8 +452,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla return; } //处理有名字但是无@符号,有@但是名字对不上的情况 - const a = value.indexOf(username)===-1; - const b = value.charAt(value.indexOf(username)-1) !="@"; if(value.indexOf(username)===-1 || value.charAt(value.indexOf(username)-1) !="@"){ //符合任意一种情况->踢掉这个人 不给他发消息 const list = new Set(atWhoLoginList.current); @@ -541,14 +520,14 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla }, [ editorInstance, resizeBarEl ]) + return ( + {atWhoVisible && atWhoList}
          - - {atWhoVisible && atWhoList}
          {showResizeBar ? : null} From cc2dd35ff42129002dcf1979753443820941638e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Wed, 10 Nov 2021 20:45:23 +0800 Subject: [PATCH 21/28] =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E6=9C=80=E5=90=8E=E4=B8=80=E8=A1=8C@?= =?UTF-8?q?=E5=BC=B9=E6=A1=86=E8=A2=AB=E9=81=AE=E6=8C=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forge/Merge/UpdateMerge.js | 2 +- src/forge/Merge/merge.css | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/forge/Merge/UpdateMerge.js b/src/forge/Merge/UpdateMerge.js index 8918db6e..8af84941 100644 --- a/src/forge/Merge/UpdateMerge.js +++ b/src/forge/Merge/UpdateMerge.js @@ -54,7 +54,7 @@ class UpdateMerge extends Component { const { data, isSpin, pull, merge } = this.state; return (
          -
          +
          {" "} {data ? ( diff --git a/src/forge/Merge/merge.css b/src/forge/Merge/merge.css index d61543b4..0ac43c7d 100644 --- a/src/forge/Merge/merge.css +++ b/src/forge/Merge/merge.css @@ -222,4 +222,8 @@ form .ant-cascader-picker, form .ant-select { .overlihide li{ max-width: 450px; + } + /* 距离底部加大 @列表被遮挡 */ + .updateMerge{ + margin: 30px auto 60px; } \ No newline at end of file From a94d02bb8a8232bc94905a38c7a0edba645bf2cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Thu, 11 Nov 2021 11:31:42 +0800 Subject: [PATCH 22/28] =?UTF-8?q?atwo=20markdown=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tpm/challengesnew/tpm-md-editor.js | 88 ++++++++++++++----- 1 file changed, 65 insertions(+), 23 deletions(-) diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index 988e9381..46c01ec3 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -131,16 +131,18 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const cursor = cm.doc.getCursor(); const line = cursor.line;//行 const ch = cursor.ch;//列 - const startIndex = cm.getRange({line,ch:0},{line,ch}).lastIndexOf("@")+1; - //替换最后的内容 - cm.replaceRange(username+" ",{line,ch:startIndex},{line,ch}); + const startIndex = cm.getRange({line,ch:0},{line,ch}).lastIndexOf("@"); + let selectUserLogin = undefined; + users.map((item)=>{ + item.username === username && (selectUserLogin = item.login); + }) + //替换内容 + cm.replaceRange("[@"+username+"]"+`(/${selectUserLogin}) `,{line,ch:startIndex},{line,ch}); //鼠标聚焦 cm.focus(); //将此user的login存储到atWhoLoginList集合中 const list = new Set(atWhoLoginList.current); - users.map((item)=>{ - item.username === username && list.add(item.login); - }) + list.add(selectUserLogin); atWhoLoginList.current = Array.from(list); setAtWhoLoginListState(Array.from(list)); } @@ -174,7 +176,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla allUsers.map(item=>{ if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 - del[del.length] = `@${item.username}`; + del[del.length] = `[@${item.username}](/${item.login})`; } }) del.length!=0 && del.map(str=>{ @@ -411,6 +413,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //当内容发生改变并且有已@列表时 if(atWhoLoginList.current.length != 0){ const codemirror = editorInstance.cm; + //startValue:触发change方法时的内容,value:处理了初始内容带@用户的情况 + let startValue = codemirror.getValue(); let value = codemirror.getValue(); //处理初始内容就自带@谁的情况 if(initValue){ @@ -418,7 +422,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla allUsers.map(item=>{ if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 - del[del.length] = `@${item.username}`; + del[del.length] = `[@${item.username}](/${item.login})`; } }) del.length!=0 && del.map(str=>{ @@ -434,26 +438,64 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla } }) }); - if(value.indexOf("@") === -1){ - //已经有要@的列表,但是没有@符号 -> 清空@集合 - atWhoLoginList.current = []; - setAtWhoLoginListState([]); - return; - } const cursor = codemirror.doc.getCursor(); const line = cursor.line; const ch = cursor.ch; - const lineContent = codemirror.getLine(line); - if(lineContent && lineContent.indexOf("@") != -1){//此行有@谁 + //处理全部内容中不包含“@”的情况 + if(value.indexOf("@") === -1){ + //markdown嵌套的链接删掉 + // Array.from(atWhoMap.keys()).map(username=>{ + // startValue = startValue.replaceAll(`[${username}](/${atWhoMap.get(username)}) `,username); + // }) + //替换全部内容 + // codemirror.setValue(startValue); + //全部内容已经有要@的列表,但是没有@符号 -> 清空@集合 + atWhoLoginList.current = []; + setAtWhoLoginListState([]); + } + + //截取第一个字符到光标的内容 + const curAfterCont = codemirror.getRange({line,ch:0},{line,ch}); + const content = codemirror.getLine(line); + //处理光标所在行 有“@”的情况 + if(content && content.indexOf("@") !== -1){ Array.from(atWhoMap.keys()).map(username=>{ - //判断lineContent是不是以列表中的某个username结尾 - if(lineContent.endsWith(username)){ - codemirror.setSelection({line,ch:ch-username.length-1},{line,ch}); - return; + //判断content是不是以列表中的某个username结尾 + const userCont = `[@${username}](/${atWhoMap.get(username)})`; + //删除空格->选中@用户区域 + if(curAfterCont.endsWith(userCont)){ + codemirror.setSelection({line,ch:curAfterCont.lastIndexOf("@")-1},{line,ch}); } - //处理有名字但是无@符号,有@但是名字对不上的情况 - if(value.indexOf(username)===-1 || value.charAt(value.indexOf(username)-1) !="@"){ - //符合任意一种情况->踢掉这个人 不给他发消息 + //处理已经有@列表但是value中不包含完整[@用户名](/login)的情况 + if(value.indexOf(userCont)===-1){ + // //markdown嵌套的链接删掉,删[]、()的情况不用处理,markdown会自动认为不是链接 + // //找到[和)的index,将区域内容替换成[]包裹的内容 + // //光标之后的内容 + // const curLeterCont = codemirror.getRange({line,ch},{line,ch:content.length}); + // console.log('光标之后的内容curLeterCont',curLeterCont); + // //删除用户名 -> ]在curLeterCont中 + // //删除login -> ]在curAfterCont中 + // const a = curAfterCont.lastIndexOf('['); + // const b = curLeterCont.indexOf(')') + // const c = curLeterCont.indexOf(']') === -1 ? curAfterCont.lastIndexOf(']') : curLeterCont.indexOf(']')+curAfterCont.length; + // console.log('[',a,')',b,']',c); + // const newCont = codemirror.getRange({line,ch:a+1},{line,ch:c}); + // console.log('newCont',newCont); + // codemirror.replaceRange(newCont,{line,ch:a-1},{line,ch:b+curAfterCont.length+1}) + + //符合情况->踢掉这个人 不给他发消息 + const list = new Set(atWhoLoginList.current); + list.delete(atWhoMap.get(username)); + atWhoLoginList.current = Array.from(list); + setAtWhoLoginListState(Array.from(list)); + } + }) + }else{ + //处理所在行没有“@”的情况 + Array.from(atWhoMap.keys()).map(username=>{ + const userCont = `[@${username}](/${atWhoMap.get(username)})`; + if(value.indexOf(userCont)===-1){ + //符合情况->踢掉这个人 不给他发消息 const list = new Set(atWhoLoginList.current); list.delete(atWhoMap.get(username)); atWhoLoginList.current = Array.from(list); From 07101f57700670efa30b0ee124542f99835af684 Mon Sep 17 00:00:00 2001 From: hucong <1422588487@qq.com> Date: Fri, 12 Nov 2021 10:29:53 +0800 Subject: [PATCH 23/28] =?UTF-8?q?=E9=A1=B9=E7=9B=AEtab=E6=A0=8Fwiki?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=AD=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forge/Main/sub/DetailBanner.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/forge/Main/sub/DetailBanner.jsx b/src/forge/Main/sub/DetailBanner.jsx index 22a5ce41..e94f6389 100644 --- a/src/forge/Main/sub/DetailBanner.jsx +++ b/src/forge/Main/sub/DetailBanner.jsx @@ -71,7 +71,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
        • - Wiki + 维基(Wiki)
        • } From fbfd5f175db44dba9a425b35f585306196be4f62 Mon Sep 17 00:00:00 2001 From: hucong <1422588487@qq.com> Date: Fri, 12 Nov 2021 10:34:56 +0800 Subject: [PATCH 24/28] =?UTF-8?q?=E9=A1=B9=E7=9B=AEtab=E6=A0=8F=E5=90=88?= =?UTF-8?q?=E5=B9=B6=E8=AF=B7=E6=B1=82=E5=A2=9E=E5=8A=A0=E8=8B=B1=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forge/Main/sub/DetailBanner.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/forge/Main/sub/DetailBanner.jsx b/src/forge/Main/sub/DetailBanner.jsx index e94f6389..ceba5208 100644 --- a/src/forge/Main/sub/DetailBanner.jsx +++ b/src/forge/Main/sub/DetailBanner.jsx @@ -61,7 +61,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
        • - 合并请求 + 合并请求(PR) {projectDetail && projectDetail.pull_requests_count ? {numFormat(projectDetail.pull_requests_count)} : ""}
        • :"" From 7ff29fec70eaed6948eff0447280b9cf237fe86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Fri, 12 Nov 2021 13:50:35 +0800 Subject: [PATCH 25/28] =?UTF-8?q?=E6=9C=AA=E5=8F=8A=E6=97=B6=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E8=AF=84=E8=AE=BA=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forge/comments/comments.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/forge/comments/comments.js b/src/forge/comments/comments.js index 8e514e16..fb19e1da 100644 --- a/src/forge/comments/comments.js +++ b/src/forge/comments/comments.js @@ -253,18 +253,22 @@ class comments extends Component { onContentChange = (value) => { if (value) { this.setState({ - content: value, quillFlag: false, }); } + this.setState({ + content: value, + }); }; replyContentChange = (value) => { if (value) { this.setState({ - reply_content: value, quillFlag: false, }); } + this.setState({ + reply_content: value, + }); }; //评论中at谁列表(存储:login) From c48e3584c3884ac07cd53767135d4e76e3746f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Fri, 12 Nov 2021 14:17:06 +0800 Subject: [PATCH 26/28] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B9=8B=E5=89=8Datwo.?= =?UTF-8?q?js=E7=9A=84=E5=BC=95=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/index.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/public/index.html b/public/index.html index 7774b2c5..9489fe42 100755 --- a/public/index.html +++ b/public/index.html @@ -13,7 +13,6 @@ - <%= htmlWebpackPlugin.tags.headTags %> @@ -26,9 +25,6 @@ - - - <%= htmlWebpackPlugin.tags.bodyTags %> \ No newline at end of file From f0747eab0534d60ad4edbb2930512f0fe9aafe5a Mon Sep 17 00:00:00 2001 From: hucong <1422588487@qq.com> Date: Tue, 16 Nov 2021 18:28:30 +0800 Subject: [PATCH 27/28] =?UTF-8?q?=E6=96=87=E6=A1=88=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forge/Settings/Setting.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/forge/Settings/Setting.js b/src/forge/Settings/Setting.js index 86383fc7..e7fc4d96 100644 --- a/src/forge/Settings/Setting.js +++ b/src/forge/Settings/Setting.js @@ -13,9 +13,9 @@ const menu = [ {name:"主页",index:"home"}, {name:"代码库",index:"code"}, {name:"易修 (Issue)",index:"issues"}, - {name:"合并请求",index:"pulls"}, - {name:"Wiki",index:"wiki"}, - {name:"工作流(beta版)",index:"devops"}, + {name:"合并请求 (PR)",index:"pulls"}, + {name:"维基 (Wiki)",index:"wiki"}, + {name:"引擎 (Engine)",index:"devops"}, // {name:"资源库",index:"resources"}, {name:"里程碑",index:"versions"}, {name:"动态",index:"activity"}, From 2617831a9f89bca452ec4bba39ad5df6f9012d21 Mon Sep 17 00:00:00 2001 From: hucong <1422588487@qq.com> Date: Tue, 16 Nov 2021 18:29:55 +0800 Subject: [PATCH 28/28] =?UTF-8?q?=E6=96=87=E6=A1=88=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forge/Main/sub/DetailBanner.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/forge/Main/sub/DetailBanner.jsx b/src/forge/Main/sub/DetailBanner.jsx index ceba5208..7ed723c7 100644 --- a/src/forge/Main/sub/DetailBanner.jsx +++ b/src/forge/Main/sub/DetailBanner.jsx @@ -80,7 +80,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
        • {/* */} - 工作流(beta版) + 引擎(Engine) {projectDetail && projectDetail.ops_count ? {projectDetail.ops_count} : ""}