@gitlab/at.js 1.5.6 → 1.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/css/jquery.atwho.min.css +1 -1
- package/dist/js/jquery.atwho.js +389 -275
- package/dist/js/jquery.atwho.min.js +1 -1
- package/package.json +15 -17
- package/CHANGELOG.md +0 -314
package/dist/js/jquery.atwho.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @gitlab/at.js - 1.5.
|
|
3
|
-
* Copyright (c)
|
|
2
|
+
* @gitlab/at.js - 1.5.8
|
|
3
|
+
* Copyright (c) 2026 chord.luo <chord.luo@gmail.com>;
|
|
4
4
|
* Homepage: https://gitlab.com/gitlab-org/frontend/At.js
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
@@ -37,20 +37,48 @@ KEY_CODE = {
|
|
|
37
37
|
SPACE: 32
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
+
// Functions set for handling and rendering the data.
|
|
41
|
+
// Others developers can override these methods to tweak At.js such as matcher.
|
|
42
|
+
// We can override them in `callbacks` settings.
|
|
43
|
+
|
|
44
|
+
// @mixin
|
|
45
|
+
|
|
46
|
+
// The context of these functions is `$.atwho.Controller` object and they are called in this sequences:
|
|
47
|
+
|
|
48
|
+
// [beforeSave, matcher, filter, remoteFilter, sorter, tplEvl, highlighter, beforeInsert, afterMatchFailed]
|
|
49
|
+
|
|
40
50
|
DEFAULT_CALLBACKS = {
|
|
51
|
+
// It would be called to restructure the data before At.js invokes `Model#save` to save data
|
|
52
|
+
// By default, At.js will convert it to a Hash Array.
|
|
53
|
+
|
|
54
|
+
// @param data [Array] data to refacotor.
|
|
55
|
+
// @return [Array] Data after refactor.
|
|
41
56
|
beforeSave: function(data) {
|
|
42
57
|
return Controller.arrayToDefaultHash(data);
|
|
43
58
|
},
|
|
59
|
+
// It would be called to match the `flag`.
|
|
60
|
+
// It will match at start of line or after whitespace
|
|
61
|
+
|
|
62
|
+
// @param flag [String] current `flag` ("@", etc)
|
|
63
|
+
// @param subtext [String] Text from start to current caret position.
|
|
64
|
+
// @param should_startWithSpace [boolean] accept white space as beginning of match.
|
|
65
|
+
// @param acceptSpaceBar [boolean] accept a space bar in the center of match,
|
|
66
|
+
// so you can match a first and last name, for ex.
|
|
67
|
+
|
|
68
|
+
// @return [String | null] Matched result.
|
|
44
69
|
matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) {
|
|
45
70
|
var _a, _y, match, regexp, space;
|
|
71
|
+
// escape RegExp
|
|
46
72
|
flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
|
47
73
|
if (should_startWithSpace) {
|
|
48
74
|
flag = '(?:^|\\s)' + flag;
|
|
49
75
|
}
|
|
76
|
+
// À
|
|
50
77
|
_a = decodeURI("%C3%80");
|
|
78
|
+
// ÿ
|
|
51
79
|
_y = decodeURI("%C3%BF");
|
|
52
80
|
space = acceptSpaceBar ? "\ " : "";
|
|
53
|
-
regexp = new RegExp(flag
|
|
81
|
+
regexp = new RegExp(`${flag}([A-Za-z${_a}-${_y}0-9_${space}\'\.\+\-]*)$|${flag}([^\\x00-\\xff]*)$`, 'gi');
|
|
54
82
|
match = regexp.exec(subtext);
|
|
55
83
|
if (match) {
|
|
56
84
|
return match[2] || match[1];
|
|
@@ -58,8 +86,18 @@ DEFAULT_CALLBACKS = {
|
|
|
58
86
|
return null;
|
|
59
87
|
}
|
|
60
88
|
},
|
|
89
|
+
// ---------------------
|
|
90
|
+
|
|
91
|
+
// Filter data by matched string.
|
|
92
|
+
|
|
93
|
+
// @param query [String] Matched string.
|
|
94
|
+
// @param data [Array] data list
|
|
95
|
+
// @param searchKey [String] at char for searching.
|
|
96
|
+
|
|
97
|
+
// @return [Array] result data.
|
|
61
98
|
filter: function(query, data, searchKey) {
|
|
62
99
|
var _results, i, item, len;
|
|
100
|
+
// !!null #=> false; !!undefined #=> false; !!'' #=> false;
|
|
63
101
|
_results = [];
|
|
64
102
|
for (i = 0, len = data.length; i < len; i++) {
|
|
65
103
|
item = data[i];
|
|
@@ -69,7 +107,24 @@ DEFAULT_CALLBACKS = {
|
|
|
69
107
|
}
|
|
70
108
|
return _results;
|
|
71
109
|
},
|
|
110
|
+
// If a function is given, At.js will invoke it if local filter can not find any data
|
|
111
|
+
|
|
112
|
+
// @param params [String] matched query
|
|
113
|
+
// @param callback [Function] callback to render page.
|
|
72
114
|
remoteFilter: null,
|
|
115
|
+
// remoteFilter: (query, callback) ->
|
|
116
|
+
// $.ajax url,
|
|
117
|
+
// data: params
|
|
118
|
+
// success: (data) ->
|
|
119
|
+
// callback(data)
|
|
120
|
+
|
|
121
|
+
// Sorter data of course.
|
|
122
|
+
|
|
123
|
+
// @param query [String] matched string
|
|
124
|
+
// @param items [Array] data that was refactored
|
|
125
|
+
// @param searchKey [String] at char to search
|
|
126
|
+
|
|
127
|
+
// @return [Array] sorted data
|
|
73
128
|
sorter: function(query, items, searchKey) {
|
|
74
129
|
var _results, i, item, len;
|
|
75
130
|
if (!query) {
|
|
@@ -87,6 +142,12 @@ DEFAULT_CALLBACKS = {
|
|
|
87
142
|
return a.atwho_order - b.atwho_order;
|
|
88
143
|
});
|
|
89
144
|
},
|
|
145
|
+
// Evaluate the template either as a string or as a function
|
|
146
|
+
// this allows someone to pass in a set of data that needs a
|
|
147
|
+
// different template for different data results
|
|
148
|
+
|
|
149
|
+
// @param tpl [function] the template function or string
|
|
150
|
+
// @param map [Hash] Data map to eval.
|
|
90
151
|
tplEval: function(tpl, map) {
|
|
91
152
|
var error, template;
|
|
92
153
|
template = tpl;
|
|
@@ -102,6 +163,12 @@ DEFAULT_CALLBACKS = {
|
|
|
102
163
|
return "";
|
|
103
164
|
}
|
|
104
165
|
},
|
|
166
|
+
// Highlight the `matched query` string.
|
|
167
|
+
|
|
168
|
+
// @param li [String] HTML String after eval.
|
|
169
|
+
// @param query [String] matched query.
|
|
170
|
+
|
|
171
|
+
// @return [String] highlighted string.
|
|
105
172
|
highlighter: function(li, query) {
|
|
106
173
|
var regexp;
|
|
107
174
|
if (!query) {
|
|
@@ -112,19 +179,33 @@ DEFAULT_CALLBACKS = {
|
|
|
112
179
|
return '> ' + $1 + '<strong>' + $2 + '</strong>' + $3 + ' <';
|
|
113
180
|
});
|
|
114
181
|
},
|
|
182
|
+
// What to do before inserting item's value into inputor.
|
|
183
|
+
|
|
184
|
+
// @param value [String] content to insert
|
|
185
|
+
// @param $li [jQuery Object] the chosen item
|
|
186
|
+
// @param e [event Object] from the user selection (keyDown or click)
|
|
115
187
|
beforeInsert: function(value, $li, e) {
|
|
116
188
|
return value;
|
|
117
189
|
},
|
|
190
|
+
// You can adjust the menu's offset here.
|
|
191
|
+
|
|
192
|
+
// @param offset [Hash] offset will be applied to menu
|
|
193
|
+
// beforeReposition: (offset) ->
|
|
194
|
+
// offset.left += 10
|
|
195
|
+
// offset.top += 10
|
|
196
|
+
// offset
|
|
118
197
|
beforeReposition: function(offset) {
|
|
119
198
|
return offset;
|
|
120
199
|
},
|
|
121
200
|
afterMatchFailed: function(at, el) {}
|
|
122
201
|
};
|
|
123
202
|
|
|
203
|
+
// At.js central contoller(searching, matching, evaluating and rendering.)
|
|
124
204
|
var App;
|
|
125
205
|
|
|
126
|
-
App =
|
|
127
|
-
|
|
206
|
+
App = class App {
|
|
207
|
+
// @param inputor [HTML DOM Object] `input` or `textarea`
|
|
208
|
+
constructor(inputor) {
|
|
128
209
|
this.currentFlag = null;
|
|
129
210
|
this.controllers = {};
|
|
130
211
|
this.aliasMaps = {};
|
|
@@ -133,19 +214,16 @@ App = (function() {
|
|
|
133
214
|
this.listen();
|
|
134
215
|
}
|
|
135
216
|
|
|
136
|
-
|
|
217
|
+
createContainer(doc) {
|
|
137
218
|
var ref;
|
|
138
219
|
if ((ref = this.$el) != null) {
|
|
139
220
|
ref.remove();
|
|
140
221
|
}
|
|
141
222
|
return $(doc.body).append(this.$el = $("<div class='atwho-container'></div>"));
|
|
142
|
-
}
|
|
223
|
+
}
|
|
143
224
|
|
|
144
|
-
|
|
225
|
+
setupRootElement(iframe, asRoot = false) {
|
|
145
226
|
var error;
|
|
146
|
-
if (asRoot == null) {
|
|
147
|
-
asRoot = false;
|
|
148
|
-
}
|
|
149
227
|
if (iframe) {
|
|
150
228
|
this.window = iframe.contentWindow;
|
|
151
229
|
this.document = iframe.contentDocument || this.window.document;
|
|
@@ -159,14 +237,16 @@ App = (function() {
|
|
|
159
237
|
error = error1;
|
|
160
238
|
this.iframe = null;
|
|
161
239
|
if ($.fn.atwho.debug) {
|
|
162
|
-
throw new Error(
|
|
240
|
+
throw new Error(`iframe auto-discovery is failed.
|
|
241
|
+
Please use \`setIframe\` to set the target iframe manually.
|
|
242
|
+
${error}`);
|
|
163
243
|
}
|
|
164
244
|
}
|
|
165
245
|
}
|
|
166
246
|
return this.createContainer((this.iframeAsRoot = asRoot) ? this.document : document);
|
|
167
|
-
}
|
|
247
|
+
}
|
|
168
248
|
|
|
169
|
-
|
|
249
|
+
controller(at) {
|
|
170
250
|
var c, current, currentFlag, ref;
|
|
171
251
|
if (this.aliasMaps[at]) {
|
|
172
252
|
current = this.controllers[this.aliasMaps[at]];
|
|
@@ -185,81 +265,76 @@ App = (function() {
|
|
|
185
265
|
} else {
|
|
186
266
|
return this.controllers[this.currentFlag];
|
|
187
267
|
}
|
|
188
|
-
}
|
|
268
|
+
}
|
|
189
269
|
|
|
190
|
-
|
|
270
|
+
setContextFor(at) {
|
|
191
271
|
this.currentFlag = at;
|
|
192
272
|
return this;
|
|
193
|
-
}
|
|
273
|
+
}
|
|
194
274
|
|
|
195
|
-
|
|
275
|
+
// At.js can register multiple at char (flag) to every inputor such as "@" and ":"
|
|
276
|
+
// Along with their own `settings` so that it works differently.
|
|
277
|
+
// After register, we still can update their `settings` such as updating `data`
|
|
278
|
+
|
|
279
|
+
// @param flag [String] at char (flag)
|
|
280
|
+
// @param settings [Hash] the settings
|
|
281
|
+
reg(flag, setting) {
|
|
196
282
|
var base, controller;
|
|
197
283
|
controller = (base = this.controllers)[flag] || (base[flag] = this.$inputor.is('[contentEditable]') ? new EditableController(this, flag) : new TextareaController(this, flag));
|
|
198
284
|
if (setting.alias) {
|
|
285
|
+
// TODO: it will produce rubbish alias map, reduse this.
|
|
199
286
|
this.aliasMaps[setting.alias] = flag;
|
|
200
287
|
}
|
|
201
288
|
controller.init(setting);
|
|
202
289
|
return this;
|
|
203
|
-
}
|
|
290
|
+
}
|
|
204
291
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
};
|
|
243
|
-
})(this)).on('scroll.atwhoInner', (function(_this) {
|
|
244
|
-
return function() {
|
|
245
|
-
var lastScrollTop;
|
|
246
|
-
lastScrollTop = _this.$inputor.scrollTop();
|
|
247
|
-
return function(e) {
|
|
248
|
-
var currentScrollTop, ref;
|
|
249
|
-
currentScrollTop = e.target.scrollTop;
|
|
250
|
-
if (lastScrollTop !== currentScrollTop) {
|
|
251
|
-
if ((ref = _this.controller()) != null) {
|
|
252
|
-
ref.view.hide(e);
|
|
253
|
-
}
|
|
292
|
+
// binding jQuery events of `inputor`'s
|
|
293
|
+
listen() {
|
|
294
|
+
return this.$inputor.on('compositionstart', (e) => {
|
|
295
|
+
var ref;
|
|
296
|
+
if ((ref = this.controller()) != null) {
|
|
297
|
+
ref.view.hide();
|
|
298
|
+
}
|
|
299
|
+
this.isComposing = true;
|
|
300
|
+
return null;
|
|
301
|
+
}).on('compositionend', (e) => {
|
|
302
|
+
this.isComposing = false;
|
|
303
|
+
setTimeout((e) => {
|
|
304
|
+
return this.dispatch(e);
|
|
305
|
+
});
|
|
306
|
+
return null;
|
|
307
|
+
}).on('keyup.atwhoInner', (e) => {
|
|
308
|
+
return this.onKeyup(e);
|
|
309
|
+
}).on('keydown.atwhoInner', (e) => {
|
|
310
|
+
return this.onKeydown(e);
|
|
311
|
+
}).on('blur.atwhoInner', (e) => {
|
|
312
|
+
var c;
|
|
313
|
+
if (c = this.controller()) {
|
|
314
|
+
c.expectedQueryCBId = null;
|
|
315
|
+
return c.view.hide(e, c.getOpt("displayTimeout"));
|
|
316
|
+
}
|
|
317
|
+
}).on('click.atwhoInner', (e) => {
|
|
318
|
+
return this.dispatch(e);
|
|
319
|
+
}).on('scroll.atwhoInner', (() => {
|
|
320
|
+
var lastScrollTop;
|
|
321
|
+
// make returned handler handle the very first call properly
|
|
322
|
+
lastScrollTop = this.$inputor.scrollTop();
|
|
323
|
+
return (e) => {
|
|
324
|
+
var currentScrollTop, ref;
|
|
325
|
+
currentScrollTop = e.target.scrollTop;
|
|
326
|
+
if (lastScrollTop !== currentScrollTop) {
|
|
327
|
+
if ((ref = this.controller()) != null) {
|
|
328
|
+
ref.view.hide(e);
|
|
254
329
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
330
|
+
}
|
|
331
|
+
lastScrollTop = currentScrollTop;
|
|
332
|
+
return true; // ensure we don't stop bubbling
|
|
258
333
|
};
|
|
259
|
-
})(
|
|
260
|
-
}
|
|
334
|
+
})());
|
|
335
|
+
}
|
|
261
336
|
|
|
262
|
-
|
|
337
|
+
shutdown() {
|
|
263
338
|
var _, c, ref;
|
|
264
339
|
ref = this.controllers;
|
|
265
340
|
for (_ in ref) {
|
|
@@ -269,9 +344,9 @@ App = (function() {
|
|
|
269
344
|
}
|
|
270
345
|
this.$inputor.off('.atwhoInner');
|
|
271
346
|
return this.$el.remove();
|
|
272
|
-
}
|
|
347
|
+
}
|
|
273
348
|
|
|
274
|
-
|
|
349
|
+
dispatch(e) {
|
|
275
350
|
var _, c, ref, results;
|
|
276
351
|
if (void 0 === e) {
|
|
277
352
|
return;
|
|
@@ -283,9 +358,9 @@ App = (function() {
|
|
|
283
358
|
results.push(c.lookUp(e));
|
|
284
359
|
}
|
|
285
360
|
return results;
|
|
286
|
-
}
|
|
361
|
+
}
|
|
287
362
|
|
|
288
|
-
|
|
363
|
+
onKeyup(e) {
|
|
289
364
|
var ref;
|
|
290
365
|
switch (e.keyCode) {
|
|
291
366
|
case KEY_CODE.ESC:
|
|
@@ -309,10 +384,12 @@ App = (function() {
|
|
|
309
384
|
default:
|
|
310
385
|
this.dispatch(e);
|
|
311
386
|
}
|
|
312
|
-
}
|
|
387
|
+
}
|
|
313
388
|
|
|
314
|
-
|
|
389
|
+
// coffeescript will return everywhere!!
|
|
390
|
+
onKeydown(e) {
|
|
315
391
|
var ref, view;
|
|
392
|
+
// return if not (view = this.controller().view).visible()
|
|
316
393
|
view = (ref = this.controller()) != null ? ref.view : void 0;
|
|
317
394
|
if (!(view && view.visible())) {
|
|
318
395
|
return;
|
|
@@ -366,21 +443,18 @@ App = (function() {
|
|
|
366
443
|
default:
|
|
367
444
|
$.noop();
|
|
368
445
|
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
return App;
|
|
446
|
+
}
|
|
372
447
|
|
|
373
|
-
}
|
|
448
|
+
};
|
|
374
449
|
|
|
375
|
-
var Controller
|
|
376
|
-
slice = [].slice;
|
|
450
|
+
var Controller;
|
|
377
451
|
|
|
378
|
-
Controller =
|
|
379
|
-
|
|
452
|
+
Controller = class Controller {
|
|
453
|
+
uid() {
|
|
380
454
|
return (Math.random().toString(16) + "000000000").substr(2, 8) + (new Date().getTime());
|
|
381
|
-
}
|
|
455
|
+
}
|
|
382
456
|
|
|
383
|
-
|
|
457
|
+
constructor(app, at1) {
|
|
384
458
|
this.app = app;
|
|
385
459
|
this.at = at1;
|
|
386
460
|
this.$inputor = this.app.$inputor;
|
|
@@ -390,53 +464,71 @@ Controller = (function() {
|
|
|
390
464
|
this.query = null;
|
|
391
465
|
this.pos = 0;
|
|
392
466
|
this.range = null;
|
|
393
|
-
if ((this.$el = $(
|
|
394
|
-
this.app.$el.append(this.$el = $(
|
|
467
|
+
if ((this.$el = $(`#atwho-ground-${this.id}`, this.app.$el)).length === 0) {
|
|
468
|
+
this.app.$el.append(this.$el = $(`<div id='atwho-ground-${this.id}'></div>`));
|
|
395
469
|
}
|
|
396
470
|
this.model = new Model(this);
|
|
397
471
|
this.view = new View(this);
|
|
398
472
|
}
|
|
399
473
|
|
|
400
|
-
|
|
401
|
-
this.setting = $.extend({}, this.setting || $.fn.atwho
|
|
474
|
+
init(setting) {
|
|
475
|
+
this.setting = $.extend({}, this.setting || $.fn.atwho.default, setting);
|
|
402
476
|
this.view.init();
|
|
403
477
|
return this.model.reload(this.setting.data);
|
|
404
|
-
}
|
|
478
|
+
}
|
|
405
479
|
|
|
406
|
-
|
|
480
|
+
destroy() {
|
|
407
481
|
this.trigger('beforeDestroy');
|
|
408
482
|
this.model.destroy();
|
|
409
483
|
this.view.destroy();
|
|
410
484
|
return this.$el.remove();
|
|
411
|
-
}
|
|
485
|
+
}
|
|
412
486
|
|
|
413
|
-
|
|
414
|
-
var
|
|
415
|
-
funcName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
|
487
|
+
callDefault(funcName, ...args) {
|
|
488
|
+
var error;
|
|
416
489
|
try {
|
|
417
490
|
return DEFAULT_CALLBACKS[funcName].apply(this, args);
|
|
418
491
|
} catch (error1) {
|
|
419
492
|
error = error1;
|
|
420
|
-
return $.error(error
|
|
493
|
+
return $.error(`${error} Or maybe At.js doesn't have function ${funcName}`);
|
|
421
494
|
}
|
|
422
|
-
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Delegate custom `jQueryEvent` to the inputor
|
|
498
|
+
// This function will add `atwho` as namespace to every jQuery event
|
|
499
|
+
// and pass current context as the last param to it.
|
|
423
500
|
|
|
424
|
-
|
|
501
|
+
// @example
|
|
502
|
+
// this.trigger "roll_n_rock", [1,2,3,4]
|
|
503
|
+
|
|
504
|
+
// $inputor.on "rool_n_rock", (e, one, two, three, four) ->
|
|
505
|
+
// console.log one, two, three, four
|
|
506
|
+
|
|
507
|
+
// @param name [String] Event name
|
|
508
|
+
// @param data [Array] data to callback
|
|
509
|
+
trigger(name, data = []) {
|
|
425
510
|
var alias, eventName;
|
|
426
|
-
if (data == null) {
|
|
427
|
-
data = [];
|
|
428
|
-
}
|
|
429
511
|
data.push(this);
|
|
430
512
|
alias = this.getOpt('alias');
|
|
431
|
-
eventName = alias ? name
|
|
513
|
+
eventName = alias ? `${name}-${alias}.atwho` : `${name}.atwho`;
|
|
432
514
|
return this.$inputor.trigger(eventName, data);
|
|
433
|
-
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Get callback either in settings which was set by plugin user or in default callbacks list.
|
|
434
518
|
|
|
435
|
-
|
|
519
|
+
// @param funcName [String] callback's name
|
|
520
|
+
// @return [Function] The callback.
|
|
521
|
+
callbacks(funcName) {
|
|
436
522
|
return this.getOpt("callbacks")[funcName] || DEFAULT_CALLBACKS[funcName];
|
|
437
|
-
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Because different registered at chars have different settings.
|
|
526
|
+
// so we should give their own for them.
|
|
438
527
|
|
|
439
|
-
|
|
528
|
+
// @param at [String] setting's at name
|
|
529
|
+
// @param default_value [?] return this if nothing is returned from current settings.
|
|
530
|
+
// @return [?] setting's value
|
|
531
|
+
getOpt(at, default_value) {
|
|
440
532
|
var e;
|
|
441
533
|
try {
|
|
442
534
|
return this.setting[at];
|
|
@@ -444,25 +536,28 @@ Controller = (function() {
|
|
|
444
536
|
e = error1;
|
|
445
537
|
return null;
|
|
446
538
|
}
|
|
447
|
-
}
|
|
539
|
+
}
|
|
448
540
|
|
|
449
|
-
|
|
541
|
+
insertContentFor($li) {
|
|
450
542
|
var data, tpl;
|
|
451
543
|
tpl = this.getOpt('insertTpl');
|
|
452
544
|
data = $.extend({}, $li.data('item-data'), {
|
|
453
545
|
'atwho-at': this.at
|
|
454
546
|
});
|
|
455
547
|
return this.callbacks("tplEval").call(this, tpl, data, "onInsert");
|
|
456
|
-
}
|
|
548
|
+
}
|
|
457
549
|
|
|
458
|
-
|
|
550
|
+
// Render list view
|
|
551
|
+
|
|
552
|
+
// @param data [Array] The data
|
|
553
|
+
renderView(data) {
|
|
459
554
|
var searchKey;
|
|
460
555
|
searchKey = this.getOpt("searchKey");
|
|
461
556
|
data = this.callbacks("sorter").call(this, this.query.text, data, searchKey);
|
|
462
557
|
return this.view.render(data.slice(0, this.getOpt('limit')));
|
|
463
|
-
}
|
|
558
|
+
}
|
|
464
559
|
|
|
465
|
-
|
|
560
|
+
static arrayToDefaultHash(data) {
|
|
466
561
|
var i, item, len, results;
|
|
467
562
|
if (!$.isArray(data)) {
|
|
468
563
|
return data;
|
|
@@ -479,9 +574,10 @@ Controller = (function() {
|
|
|
479
574
|
}
|
|
480
575
|
}
|
|
481
576
|
return results;
|
|
482
|
-
}
|
|
577
|
+
}
|
|
483
578
|
|
|
484
|
-
|
|
579
|
+
// Searching!
|
|
580
|
+
lookUp(e) {
|
|
485
581
|
var query, wait;
|
|
486
582
|
if (e && e.type === 'click' && !this.getOpt('lookUpOnClick')) {
|
|
487
583
|
return;
|
|
@@ -501,9 +597,9 @@ Controller = (function() {
|
|
|
501
597
|
this._lookUp(query);
|
|
502
598
|
}
|
|
503
599
|
return query;
|
|
504
|
-
}
|
|
600
|
+
}
|
|
505
601
|
|
|
506
|
-
|
|
602
|
+
_delayLookUp(query, wait) {
|
|
507
603
|
var now, remaining;
|
|
508
604
|
now = Date.now ? Date.now() : new Date().getTime();
|
|
509
605
|
this.previousCallTime || (this.previousCallTime = now);
|
|
@@ -511,13 +607,11 @@ Controller = (function() {
|
|
|
511
607
|
if ((0 < remaining && remaining < wait)) {
|
|
512
608
|
this.previousCallTime = now;
|
|
513
609
|
this._stopDelayedCall();
|
|
514
|
-
return this.delayedCallTimeout = setTimeout((
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
};
|
|
520
|
-
})(this), wait);
|
|
610
|
+
return this.delayedCallTimeout = setTimeout(() => {
|
|
611
|
+
this.previousCallTime = 0;
|
|
612
|
+
this.delayedCallTimeout = null;
|
|
613
|
+
return this._lookUp(query);
|
|
614
|
+
}, wait);
|
|
521
615
|
} else {
|
|
522
616
|
this._stopDelayedCall();
|
|
523
617
|
if (this.previousCallTime !== now) {
|
|
@@ -525,22 +619,23 @@ Controller = (function() {
|
|
|
525
619
|
}
|
|
526
620
|
return this._lookUp(query);
|
|
527
621
|
}
|
|
528
|
-
}
|
|
622
|
+
}
|
|
529
623
|
|
|
530
|
-
|
|
624
|
+
_stopDelayedCall() {
|
|
531
625
|
if (this.delayedCallTimeout) {
|
|
532
626
|
clearTimeout(this.delayedCallTimeout);
|
|
533
627
|
return this.delayedCallTimeout = null;
|
|
534
628
|
}
|
|
535
|
-
}
|
|
629
|
+
}
|
|
536
630
|
|
|
537
|
-
|
|
631
|
+
_generateQueryCBId() {
|
|
538
632
|
return {};
|
|
539
|
-
}
|
|
633
|
+
}
|
|
540
634
|
|
|
541
|
-
|
|
635
|
+
_lookUp(query) {
|
|
542
636
|
var _callback;
|
|
543
637
|
_callback = function(queryCBId, data) {
|
|
638
|
+
// ensure only the latest instance of this function perform actions
|
|
544
639
|
if (queryCBId !== this.expectedQueryCBId) {
|
|
545
640
|
return;
|
|
546
641
|
}
|
|
@@ -552,24 +647,17 @@ Controller = (function() {
|
|
|
552
647
|
};
|
|
553
648
|
this.expectedQueryCBId = this._generateQueryCBId();
|
|
554
649
|
return this.model.query(query.text, $.proxy(_callback, this, this.expectedQueryCBId));
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
return Controller;
|
|
650
|
+
}
|
|
558
651
|
|
|
559
|
-
}
|
|
652
|
+
};
|
|
560
653
|
|
|
561
|
-
var TextareaController
|
|
562
|
-
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; },
|
|
563
|
-
hasProp = {}.hasOwnProperty;
|
|
654
|
+
var TextareaController;
|
|
564
655
|
|
|
565
|
-
TextareaController =
|
|
566
|
-
|
|
656
|
+
TextareaController = class TextareaController extends Controller {
|
|
657
|
+
// Catch query string behind the at char
|
|
567
658
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
TextareaController.prototype.catchQuery = function() {
|
|
659
|
+
// @return [Hash] Info of the query. Look likes this: {'text': "hello", 'headPos': 0, 'endPos': 0}
|
|
660
|
+
catchQuery() {
|
|
573
661
|
var caretPos, content, end, isString, query, start, subtext;
|
|
574
662
|
content = this.$inputor.val();
|
|
575
663
|
caretPos = this.$inputor.caret('pos', {
|
|
@@ -596,9 +684,12 @@ TextareaController = (function(superClass) {
|
|
|
596
684
|
this.view.hide();
|
|
597
685
|
}
|
|
598
686
|
return this.query = query;
|
|
599
|
-
}
|
|
687
|
+
}
|
|
600
688
|
|
|
601
|
-
|
|
689
|
+
// Get offset of current at char(`flag`)
|
|
690
|
+
|
|
691
|
+
// @return [Hash] the offset which look likes this: {top: y, left: x, bottom: bottom}
|
|
692
|
+
rect() {
|
|
602
693
|
var c, iframeOffset, scaleBottom;
|
|
603
694
|
if (!(c = this.$inputor.caret('offset', this.pos - 1, {
|
|
604
695
|
iframe: this.app.iframe
|
|
@@ -616,16 +707,19 @@ TextareaController = (function(superClass) {
|
|
|
616
707
|
top: c.top,
|
|
617
708
|
bottom: c.top + c.height + scaleBottom
|
|
618
709
|
};
|
|
619
|
-
}
|
|
710
|
+
}
|
|
620
711
|
|
|
621
|
-
|
|
712
|
+
// Insert value of `data-value` attribute of chosen item into inputor
|
|
713
|
+
|
|
714
|
+
// @param content [String] string to insert
|
|
715
|
+
insert(content, $li) {
|
|
622
716
|
var $inputor, source, startStr, suffix, text;
|
|
623
717
|
$inputor = this.$inputor;
|
|
624
718
|
source = $inputor.val();
|
|
625
719
|
startStr = source.slice(0, Math.max(this.query.headPos - this.at.length, 0));
|
|
626
720
|
suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || " ";
|
|
627
721
|
content += suffix;
|
|
628
|
-
text =
|
|
722
|
+
text = `${startStr}${content}${source.slice(this.query['endPos'] || 0)}`;
|
|
629
723
|
$inputor.val(text);
|
|
630
724
|
$inputor.caret('pos', startStr.length + content.length, {
|
|
631
725
|
iframe: this.app.iframe
|
|
@@ -634,35 +728,22 @@ TextareaController = (function(superClass) {
|
|
|
634
728
|
$inputor.focus();
|
|
635
729
|
}
|
|
636
730
|
return $inputor.change();
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
return TextareaController;
|
|
640
|
-
|
|
641
|
-
})(Controller);
|
|
642
|
-
|
|
643
|
-
var EditableController,
|
|
644
|
-
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; },
|
|
645
|
-
hasProp = {}.hasOwnProperty;
|
|
731
|
+
}
|
|
646
732
|
|
|
647
|
-
|
|
648
|
-
extend(EditableController, superClass);
|
|
733
|
+
};
|
|
649
734
|
|
|
650
|
-
|
|
651
|
-
return EditableController.__super__.constructor.apply(this, arguments);
|
|
652
|
-
}
|
|
735
|
+
var EditableController;
|
|
653
736
|
|
|
654
|
-
|
|
737
|
+
EditableController = class EditableController extends Controller {
|
|
738
|
+
_getRange() {
|
|
655
739
|
var sel;
|
|
656
740
|
sel = this.app.window.getSelection();
|
|
657
741
|
if (sel.rangeCount > 0) {
|
|
658
742
|
return sel.getRangeAt(0);
|
|
659
743
|
}
|
|
660
|
-
}
|
|
744
|
+
}
|
|
661
745
|
|
|
662
|
-
|
|
663
|
-
if (range == null) {
|
|
664
|
-
range = this._getRange();
|
|
665
|
-
}
|
|
746
|
+
_setRange(position, node, range = this._getRange()) {
|
|
666
747
|
if (!(range && node)) {
|
|
667
748
|
return;
|
|
668
749
|
}
|
|
@@ -676,26 +757,23 @@ EditableController = (function(superClass) {
|
|
|
676
757
|
}
|
|
677
758
|
range.collapse(false);
|
|
678
759
|
return this._clearRange(range);
|
|
679
|
-
}
|
|
760
|
+
}
|
|
680
761
|
|
|
681
|
-
|
|
762
|
+
_clearRange(range = this._getRange()) {
|
|
682
763
|
var sel;
|
|
683
|
-
if (range == null) {
|
|
684
|
-
range = this._getRange();
|
|
685
|
-
}
|
|
686
764
|
sel = this.app.window.getSelection();
|
|
687
765
|
if (this.ctrl_a_pressed == null) {
|
|
688
766
|
sel.removeAllRanges();
|
|
689
767
|
return sel.addRange(range);
|
|
690
768
|
}
|
|
691
|
-
}
|
|
769
|
+
}
|
|
692
770
|
|
|
693
|
-
|
|
771
|
+
_movingEvent(e) {
|
|
694
772
|
var ref;
|
|
695
773
|
return e.type === 'click' || ((ref = e.which) === KEY_CODE.RIGHT || ref === KEY_CODE.LEFT || ref === KEY_CODE.UP || ref === KEY_CODE.DOWN);
|
|
696
|
-
}
|
|
774
|
+
}
|
|
697
775
|
|
|
698
|
-
|
|
776
|
+
_unwrap(node) {
|
|
699
777
|
var next;
|
|
700
778
|
node = $(node).unwrap().get(0);
|
|
701
779
|
if ((next = node.nextSibling) && next.nodeValue) {
|
|
@@ -703,9 +781,9 @@ EditableController = (function(superClass) {
|
|
|
703
781
|
$(next).remove();
|
|
704
782
|
}
|
|
705
783
|
return node;
|
|
706
|
-
}
|
|
784
|
+
}
|
|
707
785
|
|
|
708
|
-
|
|
786
|
+
catchQuery(e) {
|
|
709
787
|
var $inserted, $query, _range, index, inserted, isString, lastNode, matched, offset, query, query_content, range;
|
|
710
788
|
if (!(range = this._getRange())) {
|
|
711
789
|
return;
|
|
@@ -722,6 +800,9 @@ EditableController = (function(superClass) {
|
|
|
722
800
|
this._clearRange();
|
|
723
801
|
return;
|
|
724
802
|
}
|
|
803
|
+
// absorb range
|
|
804
|
+
// The range at the end of an element is not inside in firefox but not others browsers including IE.
|
|
805
|
+
// To normolize them, we have to move the range inside the element while deleting content or moving caret right after .atwho-inserted
|
|
725
806
|
if (/firefox/i.test(navigator.userAgent)) {
|
|
726
807
|
if ($(range.startContainer).is(this.$inputor)) {
|
|
727
808
|
this._clearRange();
|
|
@@ -741,6 +822,7 @@ EditableController = (function(superClass) {
|
|
|
741
822
|
}
|
|
742
823
|
}
|
|
743
824
|
}
|
|
825
|
+
// modifying inserted element
|
|
744
826
|
$(range.startContainer).closest('.atwho-inserted').addClass('atwho-query').siblings().removeClass('atwho-query');
|
|
745
827
|
if (($query = $(".atwho-query", this.app.document)).length > 0 && $query.is(':empty') && $query.text().length === 0) {
|
|
746
828
|
$query.remove();
|
|
@@ -760,6 +842,7 @@ EditableController = (function(superClass) {
|
|
|
760
842
|
return;
|
|
761
843
|
}
|
|
762
844
|
}
|
|
845
|
+
// matching
|
|
763
846
|
if ($query.length > 0 && (query_content = $query.attr('data-atwho-at-query'))) {
|
|
764
847
|
$query.empty().html(query_content).attr('data-atwho-at-query', null);
|
|
765
848
|
this._setRange('after', $query.get(0), range);
|
|
@@ -768,6 +851,7 @@ EditableController = (function(superClass) {
|
|
|
768
851
|
_range.setStart(range.startContainer, 0);
|
|
769
852
|
matched = this.callbacks("matcher").call(this, this.at, _range.toString(), this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));
|
|
770
853
|
isString = typeof matched === 'string';
|
|
854
|
+
// wrapping query with .atwho-query
|
|
771
855
|
if ($query.length === 0 && isString && (index = range.startOffset - this.at.length - matched.length) >= 0) {
|
|
772
856
|
range.setStart(range.startContainer, index);
|
|
773
857
|
$query = $('<span/>', this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass('atwho-query');
|
|
@@ -786,6 +870,7 @@ EditableController = (function(superClass) {
|
|
|
786
870
|
if (isString && matched.length < this.getOpt('minLen', 0)) {
|
|
787
871
|
return;
|
|
788
872
|
}
|
|
873
|
+
// handle the matched result
|
|
789
874
|
if (isString && matched.length <= this.getOpt('maxLen', 20)) {
|
|
790
875
|
query = {
|
|
791
876
|
text: matched,
|
|
@@ -807,11 +892,16 @@ EditableController = (function(superClass) {
|
|
|
807
892
|
}
|
|
808
893
|
return null;
|
|
809
894
|
}
|
|
810
|
-
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// Get offset of current at char(`flag`)
|
|
811
898
|
|
|
812
|
-
|
|
899
|
+
// @return [Hash] the offset which look likes this: {top: y, left: x, bottom: bottom}
|
|
900
|
+
rect() {
|
|
813
901
|
var $iframe, iframeOffset, rect;
|
|
814
902
|
rect = this.query.el.offset();
|
|
903
|
+
// do not use {top: 0, left: 0} from jQuery when element is hidden
|
|
904
|
+
// happens every other time the menu is displayed on click in contenteditable
|
|
815
905
|
if (!(rect && this.query.el[0].getClientRects().length)) {
|
|
816
906
|
return;
|
|
817
907
|
}
|
|
@@ -822,9 +912,12 @@ EditableController = (function(superClass) {
|
|
|
822
912
|
}
|
|
823
913
|
rect.bottom = rect.top + this.query.el.height();
|
|
824
914
|
return rect;
|
|
825
|
-
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// Insert value of `data-value` attribute of chosen item into inputor
|
|
826
918
|
|
|
827
|
-
|
|
919
|
+
// @param content [String] string to insert
|
|
920
|
+
insert(content, $li) {
|
|
828
921
|
var data, overrides, range, suffix, suffixNode;
|
|
829
922
|
if (!this.$inputor.is(':focus')) {
|
|
830
923
|
this.$inputor.focus();
|
|
@@ -848,30 +941,35 @@ EditableController = (function(superClass) {
|
|
|
848
941
|
this.$inputor.focus();
|
|
849
942
|
}
|
|
850
943
|
return this.$inputor.change();
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
return EditableController;
|
|
944
|
+
}
|
|
854
945
|
|
|
855
|
-
}
|
|
946
|
+
};
|
|
856
947
|
|
|
948
|
+
// Class to process data
|
|
857
949
|
var Model;
|
|
858
950
|
|
|
859
|
-
Model =
|
|
860
|
-
|
|
951
|
+
Model = class Model {
|
|
952
|
+
constructor(context) {
|
|
861
953
|
this.context = context;
|
|
862
954
|
this.at = this.context.at;
|
|
955
|
+
// NOTE: bind data storage to inputor maybe App class can handle it.
|
|
863
956
|
this.storage = this.context.$inputor;
|
|
864
957
|
}
|
|
865
958
|
|
|
866
|
-
|
|
959
|
+
destroy() {
|
|
867
960
|
return this.storage.data(this.at, null);
|
|
868
|
-
}
|
|
961
|
+
}
|
|
869
962
|
|
|
870
|
-
|
|
963
|
+
saved() {
|
|
871
964
|
return this.fetch() > 0;
|
|
872
|
-
}
|
|
965
|
+
}
|
|
873
966
|
|
|
874
|
-
|
|
967
|
+
// fetch data from storage by query.
|
|
968
|
+
// will invoke `callback` to return data
|
|
969
|
+
|
|
970
|
+
// @param query [String] catched string for searching
|
|
971
|
+
// @param callback [Function] for receiving data
|
|
972
|
+
query(query, callback) {
|
|
875
973
|
var _remoteFilter, data, searchKey;
|
|
876
974
|
data = this.fetch();
|
|
877
975
|
searchKey = this.context.getOpt("searchKey");
|
|
@@ -882,51 +980,66 @@ Model = (function() {
|
|
|
882
980
|
} else {
|
|
883
981
|
return _remoteFilter.call(this.context, query, callback);
|
|
884
982
|
}
|
|
885
|
-
}
|
|
983
|
+
}
|
|
886
984
|
|
|
887
|
-
|
|
985
|
+
// get or set current data which would be shown on the list view.
|
|
986
|
+
|
|
987
|
+
// @param data [Array] set data
|
|
988
|
+
// @return [Array|undefined] current data that are showing on the list view.
|
|
989
|
+
fetch() {
|
|
888
990
|
return this.storage.data(this.at) || [];
|
|
889
|
-
}
|
|
991
|
+
}
|
|
890
992
|
|
|
891
|
-
|
|
993
|
+
// save special flag's data to storage
|
|
994
|
+
|
|
995
|
+
// @param data [Array] data to save
|
|
996
|
+
save(data) {
|
|
892
997
|
return this.storage.data(this.at, this.context.callbacks("beforeSave").call(this.context, data || []));
|
|
893
|
-
}
|
|
998
|
+
}
|
|
894
999
|
|
|
895
|
-
|
|
1000
|
+
// load data. It wouldn't load for a second time if it has been loaded.
|
|
1001
|
+
|
|
1002
|
+
// @param data [Array] data to load
|
|
1003
|
+
load(data) {
|
|
896
1004
|
if (!(this.saved() || !data)) {
|
|
897
1005
|
return this._load(data);
|
|
898
1006
|
}
|
|
899
|
-
}
|
|
1007
|
+
}
|
|
900
1008
|
|
|
901
|
-
|
|
1009
|
+
reload(data) {
|
|
902
1010
|
return this._load(data);
|
|
903
|
-
}
|
|
1011
|
+
}
|
|
904
1012
|
|
|
905
|
-
|
|
1013
|
+
// load data from local or remote with callback
|
|
1014
|
+
|
|
1015
|
+
// @param data [Array|String] data to load.
|
|
1016
|
+
_load(data) {
|
|
906
1017
|
if (typeof data === "string") {
|
|
907
1018
|
throw new Error("@gitlab/at.js removed support for $.ajax");
|
|
908
1019
|
} else {
|
|
909
1020
|
return this.save(data);
|
|
910
1021
|
}
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
return Model;
|
|
1022
|
+
}
|
|
914
1023
|
|
|
915
|
-
}
|
|
1024
|
+
};
|
|
916
1025
|
|
|
1026
|
+
// View class to control how At.js's view showing.
|
|
1027
|
+
// All classes share the same DOM view.
|
|
917
1028
|
var View;
|
|
918
1029
|
|
|
919
|
-
View =
|
|
920
|
-
|
|
1030
|
+
View = class View {
|
|
1031
|
+
// @param controller [Object] The Controller.
|
|
1032
|
+
constructor(context) {
|
|
921
1033
|
this.context = context;
|
|
922
1034
|
this.$el = $("<div class='atwho-view'><ul class='atwho-view-ul'></ul></div>");
|
|
923
1035
|
this.$elUl = this.$el.children();
|
|
924
1036
|
this.timeoutID = null;
|
|
1037
|
+
// create HTML DOM of list view if it does not exist
|
|
925
1038
|
this.context.$el.append(this.$el);
|
|
926
1039
|
this.bindEvent();
|
|
927
1040
|
}
|
|
928
1041
|
|
|
929
|
-
|
|
1042
|
+
init() {
|
|
930
1043
|
var header_tpl, id;
|
|
931
1044
|
id = this.context.getOpt("alias") || this.context.at.charCodeAt(0);
|
|
932
1045
|
header_tpl = this.context.getOpt("headerTpl");
|
|
@@ -934,53 +1047,53 @@ View = (function() {
|
|
|
934
1047
|
this.$el.prepend(header_tpl);
|
|
935
1048
|
}
|
|
936
1049
|
return this.$el.attr({
|
|
937
|
-
'id':
|
|
1050
|
+
'id': `at-view-${id}`
|
|
938
1051
|
});
|
|
939
|
-
}
|
|
1052
|
+
}
|
|
940
1053
|
|
|
941
|
-
|
|
1054
|
+
destroy() {
|
|
942
1055
|
return this.$el.remove();
|
|
943
|
-
}
|
|
1056
|
+
}
|
|
944
1057
|
|
|
945
|
-
|
|
1058
|
+
bindEvent() {
|
|
946
1059
|
var $menu, lastCoordX, lastCoordY;
|
|
947
1060
|
$menu = this.$el.find('ul');
|
|
948
1061
|
lastCoordX = 0;
|
|
949
1062
|
lastCoordY = 0;
|
|
950
|
-
return $menu.on('mousemove.atwho-view', 'li', (
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
};
|
|
972
|
-
})(this));
|
|
973
|
-
};
|
|
1063
|
+
return $menu.on('mousemove.atwho-view', 'li', (e) => {
|
|
1064
|
+
var $cur;
|
|
1065
|
+
// If the mouse hasn't actually moved then exit.
|
|
1066
|
+
if (lastCoordX === e.clientX && lastCoordY === e.clientY) {
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
lastCoordX = e.clientX;
|
|
1070
|
+
lastCoordY = e.clientY;
|
|
1071
|
+
$cur = $(e.currentTarget);
|
|
1072
|
+
if ($cur.hasClass('cur')) {
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
$menu.find('.cur').removeClass('cur');
|
|
1076
|
+
return $cur.addClass('cur');
|
|
1077
|
+
}).on('click.atwho-view', 'li', (e) => {
|
|
1078
|
+
$menu.find('.cur').removeClass('cur');
|
|
1079
|
+
$(e.currentTarget).addClass('cur');
|
|
1080
|
+
this.choose(e);
|
|
1081
|
+
return e.preventDefault();
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
974
1084
|
|
|
975
|
-
|
|
1085
|
+
// Check if view is visible
|
|
1086
|
+
|
|
1087
|
+
// @return [Boolean]
|
|
1088
|
+
visible() {
|
|
976
1089
|
return $.expr.filters.visible(this.$el[0]);
|
|
977
|
-
}
|
|
1090
|
+
}
|
|
978
1091
|
|
|
979
|
-
|
|
1092
|
+
highlighted() {
|
|
980
1093
|
return this.$el.find(".cur").length > 0;
|
|
981
|
-
}
|
|
1094
|
+
}
|
|
982
1095
|
|
|
983
|
-
|
|
1096
|
+
choose(e) {
|
|
984
1097
|
var $li, content;
|
|
985
1098
|
if (($li = this.$el.find(".cur")).length) {
|
|
986
1099
|
content = this.context.insertContentFor($li);
|
|
@@ -992,9 +1105,9 @@ View = (function() {
|
|
|
992
1105
|
if (this.context.getOpt("hideWithoutSuffix")) {
|
|
993
1106
|
return this.stopShowing = true;
|
|
994
1107
|
}
|
|
995
|
-
}
|
|
1108
|
+
}
|
|
996
1109
|
|
|
997
|
-
|
|
1110
|
+
reposition(rect) {
|
|
998
1111
|
var _window, offset, overflowOffset, ref;
|
|
999
1112
|
_window = this.context.app.iframeAsRoot ? this.context.app.window : window;
|
|
1000
1113
|
if (rect.bottom + this.$el.height() - $(_window).scrollTop() > $(_window).height()) {
|
|
@@ -1012,9 +1125,9 @@ View = (function() {
|
|
|
1012
1125
|
}
|
|
1013
1126
|
this.$el.offset(offset);
|
|
1014
1127
|
return this.context.trigger("reposition", [offset]);
|
|
1015
|
-
}
|
|
1128
|
+
}
|
|
1016
1129
|
|
|
1017
|
-
|
|
1130
|
+
next() {
|
|
1018
1131
|
var cur, next, nextEl, offset;
|
|
1019
1132
|
cur = this.$el.find('.cur').removeClass('cur');
|
|
1020
1133
|
next = cur.next();
|
|
@@ -1025,9 +1138,9 @@ View = (function() {
|
|
|
1025
1138
|
nextEl = next[0];
|
|
1026
1139
|
offset = nextEl.offsetTop + nextEl.offsetHeight + (nextEl.nextSibling ? nextEl.nextSibling.offsetHeight : 0);
|
|
1027
1140
|
return this.scrollTop(Math.max(0, offset - this.$el.height()));
|
|
1028
|
-
}
|
|
1141
|
+
}
|
|
1029
1142
|
|
|
1030
|
-
|
|
1143
|
+
prev() {
|
|
1031
1144
|
var cur, offset, prev, prevEl;
|
|
1032
1145
|
cur = this.$el.find('.cur').removeClass('cur');
|
|
1033
1146
|
prev = cur.prev();
|
|
@@ -1038,13 +1151,13 @@ View = (function() {
|
|
|
1038
1151
|
prevEl = prev[0];
|
|
1039
1152
|
offset = prevEl.offsetTop + prevEl.offsetHeight + (prevEl.nextSibling ? prevEl.nextSibling.offsetHeight : 0);
|
|
1040
1153
|
return this.scrollTop(Math.max(0, offset - this.$el.height()));
|
|
1041
|
-
}
|
|
1154
|
+
}
|
|
1042
1155
|
|
|
1043
|
-
|
|
1156
|
+
scrollTop(scrollTop) {
|
|
1044
1157
|
return this.$elUl.scrollTop(scrollTop);
|
|
1045
|
-
}
|
|
1158
|
+
}
|
|
1046
1159
|
|
|
1047
|
-
|
|
1160
|
+
show() {
|
|
1048
1161
|
var rect;
|
|
1049
1162
|
if (this.stopShowing) {
|
|
1050
1163
|
this.stopShowing = false;
|
|
@@ -1058,9 +1171,9 @@ View = (function() {
|
|
|
1058
1171
|
if (rect = this.context.rect()) {
|
|
1059
1172
|
return this.reposition(rect);
|
|
1060
1173
|
}
|
|
1061
|
-
}
|
|
1174
|
+
}
|
|
1062
1175
|
|
|
1063
|
-
|
|
1176
|
+
hide(e, time) {
|
|
1064
1177
|
var callback;
|
|
1065
1178
|
if (!this.visible()) {
|
|
1066
1179
|
return;
|
|
@@ -1069,17 +1182,16 @@ View = (function() {
|
|
|
1069
1182
|
this.$el.hide();
|
|
1070
1183
|
return this.context.trigger('hidden', [e]);
|
|
1071
1184
|
} else {
|
|
1072
|
-
callback = (
|
|
1073
|
-
return
|
|
1074
|
-
|
|
1075
|
-
};
|
|
1076
|
-
})(this);
|
|
1185
|
+
callback = () => {
|
|
1186
|
+
return this.hide();
|
|
1187
|
+
};
|
|
1077
1188
|
clearTimeout(this.timeoutID);
|
|
1078
1189
|
return this.timeoutID = setTimeout(callback, time);
|
|
1079
1190
|
}
|
|
1080
|
-
}
|
|
1191
|
+
}
|
|
1081
1192
|
|
|
1082
|
-
|
|
1193
|
+
// render list view
|
|
1194
|
+
render(list) {
|
|
1083
1195
|
var $li, $ul, i, item, len, li, tpl;
|
|
1084
1196
|
if (!($.isArray(list) && list.length > 0)) {
|
|
1085
1197
|
this.hide();
|
|
@@ -1102,15 +1214,17 @@ View = (function() {
|
|
|
1102
1214
|
if (this.context.getOpt('highlightFirst')) {
|
|
1103
1215
|
return $ul.find("li:first").addClass("cur");
|
|
1104
1216
|
}
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
return View;
|
|
1217
|
+
}
|
|
1108
1218
|
|
|
1109
|
-
}
|
|
1219
|
+
};
|
|
1110
1220
|
|
|
1111
1221
|
var Api;
|
|
1112
1222
|
|
|
1113
1223
|
Api = {
|
|
1224
|
+
// load a flag's data
|
|
1225
|
+
|
|
1226
|
+
// @params at[String] the flag
|
|
1227
|
+
// @params data [Array] data to storage.
|
|
1114
1228
|
load: function(at, data) {
|
|
1115
1229
|
var c;
|
|
1116
1230
|
if (c = this.controller(at)) {
|
|
@@ -1158,7 +1272,7 @@ $.fn.atwho = function(method) {
|
|
|
1158
1272
|
} else if (Api[method] && app) {
|
|
1159
1273
|
return result = Api[method].apply(app, Array.prototype.slice.call(_args, 1));
|
|
1160
1274
|
} else {
|
|
1161
|
-
return $.error(
|
|
1275
|
+
return $.error(`Method ${method} does not exist on jQuery.atwho`);
|
|
1162
1276
|
}
|
|
1163
1277
|
});
|
|
1164
1278
|
if (result != null) {
|
|
@@ -1168,7 +1282,7 @@ $.fn.atwho = function(method) {
|
|
|
1168
1282
|
}
|
|
1169
1283
|
};
|
|
1170
1284
|
|
|
1171
|
-
$.fn.atwho
|
|
1285
|
+
$.fn.atwho.default = {
|
|
1172
1286
|
at: void 0,
|
|
1173
1287
|
alias: void 0,
|
|
1174
1288
|
data: null,
|