@gitlab/at.js 1.5.7 → 1.5.9
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 +391 -275
- package/dist/js/jquery.atwho.min.js +1 -1
- package/package.json +15 -15
- package/src/controller.coffee +3 -1
- 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.9
|
|
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,73 @@ Controller = (function() {
|
|
|
390
464
|
this.query = null;
|
|
391
465
|
this.pos = 0;
|
|
392
466
|
this.range = null;
|
|
393
|
-
if ((this.$el = $(
|
|
394
|
-
this
|
|
467
|
+
if ((this.$el = $(`#atwho-ground-${this.id}`, this.app.$el)).length === 0) {
|
|
468
|
+
this.$el = $('<div></div>');
|
|
469
|
+
this.$el.attr('id', `atwho-ground-${this.id}`);
|
|
470
|
+
this.app.$el.append(this.$el);
|
|
395
471
|
}
|
|
396
472
|
this.model = new Model(this);
|
|
397
473
|
this.view = new View(this);
|
|
398
474
|
}
|
|
399
475
|
|
|
400
|
-
|
|
401
|
-
this.setting = $.extend({}, this.setting || $.fn.atwho
|
|
476
|
+
init(setting) {
|
|
477
|
+
this.setting = $.extend({}, this.setting || $.fn.atwho.default, setting);
|
|
402
478
|
this.view.init();
|
|
403
479
|
return this.model.reload(this.setting.data);
|
|
404
|
-
}
|
|
480
|
+
}
|
|
405
481
|
|
|
406
|
-
|
|
482
|
+
destroy() {
|
|
407
483
|
this.trigger('beforeDestroy');
|
|
408
484
|
this.model.destroy();
|
|
409
485
|
this.view.destroy();
|
|
410
486
|
return this.$el.remove();
|
|
411
|
-
}
|
|
487
|
+
}
|
|
412
488
|
|
|
413
|
-
|
|
414
|
-
var
|
|
415
|
-
funcName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
|
489
|
+
callDefault(funcName, ...args) {
|
|
490
|
+
var error;
|
|
416
491
|
try {
|
|
417
492
|
return DEFAULT_CALLBACKS[funcName].apply(this, args);
|
|
418
493
|
} catch (error1) {
|
|
419
494
|
error = error1;
|
|
420
|
-
return $.error(error
|
|
495
|
+
return $.error(`${error} Or maybe At.js doesn't have function ${funcName}`);
|
|
421
496
|
}
|
|
422
|
-
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Delegate custom `jQueryEvent` to the inputor
|
|
500
|
+
// This function will add `atwho` as namespace to every jQuery event
|
|
501
|
+
// and pass current context as the last param to it.
|
|
423
502
|
|
|
424
|
-
|
|
503
|
+
// @example
|
|
504
|
+
// this.trigger "roll_n_rock", [1,2,3,4]
|
|
505
|
+
|
|
506
|
+
// $inputor.on "rool_n_rock", (e, one, two, three, four) ->
|
|
507
|
+
// console.log one, two, three, four
|
|
508
|
+
|
|
509
|
+
// @param name [String] Event name
|
|
510
|
+
// @param data [Array] data to callback
|
|
511
|
+
trigger(name, data = []) {
|
|
425
512
|
var alias, eventName;
|
|
426
|
-
if (data == null) {
|
|
427
|
-
data = [];
|
|
428
|
-
}
|
|
429
513
|
data.push(this);
|
|
430
514
|
alias = this.getOpt('alias');
|
|
431
|
-
eventName = alias ? name
|
|
515
|
+
eventName = alias ? `${name}-${alias}.atwho` : `${name}.atwho`;
|
|
432
516
|
return this.$inputor.trigger(eventName, data);
|
|
433
|
-
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Get callback either in settings which was set by plugin user or in default callbacks list.
|
|
434
520
|
|
|
435
|
-
|
|
521
|
+
// @param funcName [String] callback's name
|
|
522
|
+
// @return [Function] The callback.
|
|
523
|
+
callbacks(funcName) {
|
|
436
524
|
return this.getOpt("callbacks")[funcName] || DEFAULT_CALLBACKS[funcName];
|
|
437
|
-
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Because different registered at chars have different settings.
|
|
528
|
+
// so we should give their own for them.
|
|
438
529
|
|
|
439
|
-
|
|
530
|
+
// @param at [String] setting's at name
|
|
531
|
+
// @param default_value [?] return this if nothing is returned from current settings.
|
|
532
|
+
// @return [?] setting's value
|
|
533
|
+
getOpt(at, default_value) {
|
|
440
534
|
var e;
|
|
441
535
|
try {
|
|
442
536
|
return this.setting[at];
|
|
@@ -444,25 +538,28 @@ Controller = (function() {
|
|
|
444
538
|
e = error1;
|
|
445
539
|
return null;
|
|
446
540
|
}
|
|
447
|
-
}
|
|
541
|
+
}
|
|
448
542
|
|
|
449
|
-
|
|
543
|
+
insertContentFor($li) {
|
|
450
544
|
var data, tpl;
|
|
451
545
|
tpl = this.getOpt('insertTpl');
|
|
452
546
|
data = $.extend({}, $li.data('item-data'), {
|
|
453
547
|
'atwho-at': this.at
|
|
454
548
|
});
|
|
455
549
|
return this.callbacks("tplEval").call(this, tpl, data, "onInsert");
|
|
456
|
-
}
|
|
550
|
+
}
|
|
457
551
|
|
|
458
|
-
|
|
552
|
+
// Render list view
|
|
553
|
+
|
|
554
|
+
// @param data [Array] The data
|
|
555
|
+
renderView(data) {
|
|
459
556
|
var searchKey;
|
|
460
557
|
searchKey = this.getOpt("searchKey");
|
|
461
558
|
data = this.callbacks("sorter").call(this, this.query.text, data, searchKey);
|
|
462
559
|
return this.view.render(data.slice(0, this.getOpt('limit')));
|
|
463
|
-
}
|
|
560
|
+
}
|
|
464
561
|
|
|
465
|
-
|
|
562
|
+
static arrayToDefaultHash(data) {
|
|
466
563
|
var i, item, len, results;
|
|
467
564
|
if (!$.isArray(data)) {
|
|
468
565
|
return data;
|
|
@@ -479,9 +576,10 @@ Controller = (function() {
|
|
|
479
576
|
}
|
|
480
577
|
}
|
|
481
578
|
return results;
|
|
482
|
-
}
|
|
579
|
+
}
|
|
483
580
|
|
|
484
|
-
|
|
581
|
+
// Searching!
|
|
582
|
+
lookUp(e) {
|
|
485
583
|
var query, wait;
|
|
486
584
|
if (e && e.type === 'click' && !this.getOpt('lookUpOnClick')) {
|
|
487
585
|
return;
|
|
@@ -501,9 +599,9 @@ Controller = (function() {
|
|
|
501
599
|
this._lookUp(query);
|
|
502
600
|
}
|
|
503
601
|
return query;
|
|
504
|
-
}
|
|
602
|
+
}
|
|
505
603
|
|
|
506
|
-
|
|
604
|
+
_delayLookUp(query, wait) {
|
|
507
605
|
var now, remaining;
|
|
508
606
|
now = Date.now ? Date.now() : new Date().getTime();
|
|
509
607
|
this.previousCallTime || (this.previousCallTime = now);
|
|
@@ -511,13 +609,11 @@ Controller = (function() {
|
|
|
511
609
|
if ((0 < remaining && remaining < wait)) {
|
|
512
610
|
this.previousCallTime = now;
|
|
513
611
|
this._stopDelayedCall();
|
|
514
|
-
return this.delayedCallTimeout = setTimeout((
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
};
|
|
520
|
-
})(this), wait);
|
|
612
|
+
return this.delayedCallTimeout = setTimeout(() => {
|
|
613
|
+
this.previousCallTime = 0;
|
|
614
|
+
this.delayedCallTimeout = null;
|
|
615
|
+
return this._lookUp(query);
|
|
616
|
+
}, wait);
|
|
521
617
|
} else {
|
|
522
618
|
this._stopDelayedCall();
|
|
523
619
|
if (this.previousCallTime !== now) {
|
|
@@ -525,22 +621,23 @@ Controller = (function() {
|
|
|
525
621
|
}
|
|
526
622
|
return this._lookUp(query);
|
|
527
623
|
}
|
|
528
|
-
}
|
|
624
|
+
}
|
|
529
625
|
|
|
530
|
-
|
|
626
|
+
_stopDelayedCall() {
|
|
531
627
|
if (this.delayedCallTimeout) {
|
|
532
628
|
clearTimeout(this.delayedCallTimeout);
|
|
533
629
|
return this.delayedCallTimeout = null;
|
|
534
630
|
}
|
|
535
|
-
}
|
|
631
|
+
}
|
|
536
632
|
|
|
537
|
-
|
|
633
|
+
_generateQueryCBId() {
|
|
538
634
|
return {};
|
|
539
|
-
}
|
|
635
|
+
}
|
|
540
636
|
|
|
541
|
-
|
|
637
|
+
_lookUp(query) {
|
|
542
638
|
var _callback;
|
|
543
639
|
_callback = function(queryCBId, data) {
|
|
640
|
+
// ensure only the latest instance of this function perform actions
|
|
544
641
|
if (queryCBId !== this.expectedQueryCBId) {
|
|
545
642
|
return;
|
|
546
643
|
}
|
|
@@ -552,24 +649,17 @@ Controller = (function() {
|
|
|
552
649
|
};
|
|
553
650
|
this.expectedQueryCBId = this._generateQueryCBId();
|
|
554
651
|
return this.model.query(query.text, $.proxy(_callback, this, this.expectedQueryCBId));
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
return Controller;
|
|
652
|
+
}
|
|
558
653
|
|
|
559
|
-
}
|
|
654
|
+
};
|
|
560
655
|
|
|
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;
|
|
656
|
+
var TextareaController;
|
|
564
657
|
|
|
565
|
-
TextareaController =
|
|
566
|
-
|
|
658
|
+
TextareaController = class TextareaController extends Controller {
|
|
659
|
+
// Catch query string behind the at char
|
|
567
660
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
TextareaController.prototype.catchQuery = function() {
|
|
661
|
+
// @return [Hash] Info of the query. Look likes this: {'text': "hello", 'headPos': 0, 'endPos': 0}
|
|
662
|
+
catchQuery() {
|
|
573
663
|
var caretPos, content, end, isString, query, start, subtext;
|
|
574
664
|
content = this.$inputor.val();
|
|
575
665
|
caretPos = this.$inputor.caret('pos', {
|
|
@@ -596,9 +686,12 @@ TextareaController = (function(superClass) {
|
|
|
596
686
|
this.view.hide();
|
|
597
687
|
}
|
|
598
688
|
return this.query = query;
|
|
599
|
-
}
|
|
689
|
+
}
|
|
600
690
|
|
|
601
|
-
|
|
691
|
+
// Get offset of current at char(`flag`)
|
|
692
|
+
|
|
693
|
+
// @return [Hash] the offset which look likes this: {top: y, left: x, bottom: bottom}
|
|
694
|
+
rect() {
|
|
602
695
|
var c, iframeOffset, scaleBottom;
|
|
603
696
|
if (!(c = this.$inputor.caret('offset', this.pos - 1, {
|
|
604
697
|
iframe: this.app.iframe
|
|
@@ -616,16 +709,19 @@ TextareaController = (function(superClass) {
|
|
|
616
709
|
top: c.top,
|
|
617
710
|
bottom: c.top + c.height + scaleBottom
|
|
618
711
|
};
|
|
619
|
-
}
|
|
712
|
+
}
|
|
620
713
|
|
|
621
|
-
|
|
714
|
+
// Insert value of `data-value` attribute of chosen item into inputor
|
|
715
|
+
|
|
716
|
+
// @param content [String] string to insert
|
|
717
|
+
insert(content, $li) {
|
|
622
718
|
var $inputor, source, startStr, suffix, text;
|
|
623
719
|
$inputor = this.$inputor;
|
|
624
720
|
source = $inputor.val();
|
|
625
721
|
startStr = source.slice(0, Math.max(this.query.headPos - this.at.length, 0));
|
|
626
722
|
suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || " ";
|
|
627
723
|
content += suffix;
|
|
628
|
-
text =
|
|
724
|
+
text = `${startStr}${content}${source.slice(this.query['endPos'] || 0)}`;
|
|
629
725
|
$inputor.val(text);
|
|
630
726
|
$inputor.caret('pos', startStr.length + content.length, {
|
|
631
727
|
iframe: this.app.iframe
|
|
@@ -634,35 +730,22 @@ TextareaController = (function(superClass) {
|
|
|
634
730
|
$inputor.focus();
|
|
635
731
|
}
|
|
636
732
|
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;
|
|
733
|
+
}
|
|
646
734
|
|
|
647
|
-
|
|
648
|
-
extend(EditableController, superClass);
|
|
735
|
+
};
|
|
649
736
|
|
|
650
|
-
|
|
651
|
-
return EditableController.__super__.constructor.apply(this, arguments);
|
|
652
|
-
}
|
|
737
|
+
var EditableController;
|
|
653
738
|
|
|
654
|
-
|
|
739
|
+
EditableController = class EditableController extends Controller {
|
|
740
|
+
_getRange() {
|
|
655
741
|
var sel;
|
|
656
742
|
sel = this.app.window.getSelection();
|
|
657
743
|
if (sel.rangeCount > 0) {
|
|
658
744
|
return sel.getRangeAt(0);
|
|
659
745
|
}
|
|
660
|
-
}
|
|
746
|
+
}
|
|
661
747
|
|
|
662
|
-
|
|
663
|
-
if (range == null) {
|
|
664
|
-
range = this._getRange();
|
|
665
|
-
}
|
|
748
|
+
_setRange(position, node, range = this._getRange()) {
|
|
666
749
|
if (!(range && node)) {
|
|
667
750
|
return;
|
|
668
751
|
}
|
|
@@ -676,26 +759,23 @@ EditableController = (function(superClass) {
|
|
|
676
759
|
}
|
|
677
760
|
range.collapse(false);
|
|
678
761
|
return this._clearRange(range);
|
|
679
|
-
}
|
|
762
|
+
}
|
|
680
763
|
|
|
681
|
-
|
|
764
|
+
_clearRange(range = this._getRange()) {
|
|
682
765
|
var sel;
|
|
683
|
-
if (range == null) {
|
|
684
|
-
range = this._getRange();
|
|
685
|
-
}
|
|
686
766
|
sel = this.app.window.getSelection();
|
|
687
767
|
if (this.ctrl_a_pressed == null) {
|
|
688
768
|
sel.removeAllRanges();
|
|
689
769
|
return sel.addRange(range);
|
|
690
770
|
}
|
|
691
|
-
}
|
|
771
|
+
}
|
|
692
772
|
|
|
693
|
-
|
|
773
|
+
_movingEvent(e) {
|
|
694
774
|
var ref;
|
|
695
775
|
return e.type === 'click' || ((ref = e.which) === KEY_CODE.RIGHT || ref === KEY_CODE.LEFT || ref === KEY_CODE.UP || ref === KEY_CODE.DOWN);
|
|
696
|
-
}
|
|
776
|
+
}
|
|
697
777
|
|
|
698
|
-
|
|
778
|
+
_unwrap(node) {
|
|
699
779
|
var next;
|
|
700
780
|
node = $(node).unwrap().get(0);
|
|
701
781
|
if ((next = node.nextSibling) && next.nodeValue) {
|
|
@@ -703,9 +783,9 @@ EditableController = (function(superClass) {
|
|
|
703
783
|
$(next).remove();
|
|
704
784
|
}
|
|
705
785
|
return node;
|
|
706
|
-
}
|
|
786
|
+
}
|
|
707
787
|
|
|
708
|
-
|
|
788
|
+
catchQuery(e) {
|
|
709
789
|
var $inserted, $query, _range, index, inserted, isString, lastNode, matched, offset, query, query_content, range;
|
|
710
790
|
if (!(range = this._getRange())) {
|
|
711
791
|
return;
|
|
@@ -722,6 +802,9 @@ EditableController = (function(superClass) {
|
|
|
722
802
|
this._clearRange();
|
|
723
803
|
return;
|
|
724
804
|
}
|
|
805
|
+
// absorb range
|
|
806
|
+
// The range at the end of an element is not inside in firefox but not others browsers including IE.
|
|
807
|
+
// To normolize them, we have to move the range inside the element while deleting content or moving caret right after .atwho-inserted
|
|
725
808
|
if (/firefox/i.test(navigator.userAgent)) {
|
|
726
809
|
if ($(range.startContainer).is(this.$inputor)) {
|
|
727
810
|
this._clearRange();
|
|
@@ -741,6 +824,7 @@ EditableController = (function(superClass) {
|
|
|
741
824
|
}
|
|
742
825
|
}
|
|
743
826
|
}
|
|
827
|
+
// modifying inserted element
|
|
744
828
|
$(range.startContainer).closest('.atwho-inserted').addClass('atwho-query').siblings().removeClass('atwho-query');
|
|
745
829
|
if (($query = $(".atwho-query", this.app.document)).length > 0 && $query.is(':empty') && $query.text().length === 0) {
|
|
746
830
|
$query.remove();
|
|
@@ -760,6 +844,7 @@ EditableController = (function(superClass) {
|
|
|
760
844
|
return;
|
|
761
845
|
}
|
|
762
846
|
}
|
|
847
|
+
// matching
|
|
763
848
|
if ($query.length > 0 && (query_content = $query.attr('data-atwho-at-query'))) {
|
|
764
849
|
$query.empty().html(query_content).attr('data-atwho-at-query', null);
|
|
765
850
|
this._setRange('after', $query.get(0), range);
|
|
@@ -768,6 +853,7 @@ EditableController = (function(superClass) {
|
|
|
768
853
|
_range.setStart(range.startContainer, 0);
|
|
769
854
|
matched = this.callbacks("matcher").call(this, this.at, _range.toString(), this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));
|
|
770
855
|
isString = typeof matched === 'string';
|
|
856
|
+
// wrapping query with .atwho-query
|
|
771
857
|
if ($query.length === 0 && isString && (index = range.startOffset - this.at.length - matched.length) >= 0) {
|
|
772
858
|
range.setStart(range.startContainer, index);
|
|
773
859
|
$query = $('<span/>', this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass('atwho-query');
|
|
@@ -786,6 +872,7 @@ EditableController = (function(superClass) {
|
|
|
786
872
|
if (isString && matched.length < this.getOpt('minLen', 0)) {
|
|
787
873
|
return;
|
|
788
874
|
}
|
|
875
|
+
// handle the matched result
|
|
789
876
|
if (isString && matched.length <= this.getOpt('maxLen', 20)) {
|
|
790
877
|
query = {
|
|
791
878
|
text: matched,
|
|
@@ -807,11 +894,16 @@ EditableController = (function(superClass) {
|
|
|
807
894
|
}
|
|
808
895
|
return null;
|
|
809
896
|
}
|
|
810
|
-
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// Get offset of current at char(`flag`)
|
|
811
900
|
|
|
812
|
-
|
|
901
|
+
// @return [Hash] the offset which look likes this: {top: y, left: x, bottom: bottom}
|
|
902
|
+
rect() {
|
|
813
903
|
var $iframe, iframeOffset, rect;
|
|
814
904
|
rect = this.query.el.offset();
|
|
905
|
+
// do not use {top: 0, left: 0} from jQuery when element is hidden
|
|
906
|
+
// happens every other time the menu is displayed on click in contenteditable
|
|
815
907
|
if (!(rect && this.query.el[0].getClientRects().length)) {
|
|
816
908
|
return;
|
|
817
909
|
}
|
|
@@ -822,9 +914,12 @@ EditableController = (function(superClass) {
|
|
|
822
914
|
}
|
|
823
915
|
rect.bottom = rect.top + this.query.el.height();
|
|
824
916
|
return rect;
|
|
825
|
-
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// Insert value of `data-value` attribute of chosen item into inputor
|
|
826
920
|
|
|
827
|
-
|
|
921
|
+
// @param content [String] string to insert
|
|
922
|
+
insert(content, $li) {
|
|
828
923
|
var data, overrides, range, suffix, suffixNode;
|
|
829
924
|
if (!this.$inputor.is(':focus')) {
|
|
830
925
|
this.$inputor.focus();
|
|
@@ -848,30 +943,35 @@ EditableController = (function(superClass) {
|
|
|
848
943
|
this.$inputor.focus();
|
|
849
944
|
}
|
|
850
945
|
return this.$inputor.change();
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
return EditableController;
|
|
946
|
+
}
|
|
854
947
|
|
|
855
|
-
}
|
|
948
|
+
};
|
|
856
949
|
|
|
950
|
+
// Class to process data
|
|
857
951
|
var Model;
|
|
858
952
|
|
|
859
|
-
Model =
|
|
860
|
-
|
|
953
|
+
Model = class Model {
|
|
954
|
+
constructor(context) {
|
|
861
955
|
this.context = context;
|
|
862
956
|
this.at = this.context.at;
|
|
957
|
+
// NOTE: bind data storage to inputor maybe App class can handle it.
|
|
863
958
|
this.storage = this.context.$inputor;
|
|
864
959
|
}
|
|
865
960
|
|
|
866
|
-
|
|
961
|
+
destroy() {
|
|
867
962
|
return this.storage.data(this.at, null);
|
|
868
|
-
}
|
|
963
|
+
}
|
|
869
964
|
|
|
870
|
-
|
|
965
|
+
saved() {
|
|
871
966
|
return this.fetch() > 0;
|
|
872
|
-
}
|
|
967
|
+
}
|
|
873
968
|
|
|
874
|
-
|
|
969
|
+
// fetch data from storage by query.
|
|
970
|
+
// will invoke `callback` to return data
|
|
971
|
+
|
|
972
|
+
// @param query [String] catched string for searching
|
|
973
|
+
// @param callback [Function] for receiving data
|
|
974
|
+
query(query, callback) {
|
|
875
975
|
var _remoteFilter, data, searchKey;
|
|
876
976
|
data = this.fetch();
|
|
877
977
|
searchKey = this.context.getOpt("searchKey");
|
|
@@ -882,51 +982,66 @@ Model = (function() {
|
|
|
882
982
|
} else {
|
|
883
983
|
return _remoteFilter.call(this.context, query, callback);
|
|
884
984
|
}
|
|
885
|
-
}
|
|
985
|
+
}
|
|
886
986
|
|
|
887
|
-
|
|
987
|
+
// get or set current data which would be shown on the list view.
|
|
988
|
+
|
|
989
|
+
// @param data [Array] set data
|
|
990
|
+
// @return [Array|undefined] current data that are showing on the list view.
|
|
991
|
+
fetch() {
|
|
888
992
|
return this.storage.data(this.at) || [];
|
|
889
|
-
}
|
|
993
|
+
}
|
|
890
994
|
|
|
891
|
-
|
|
995
|
+
// save special flag's data to storage
|
|
996
|
+
|
|
997
|
+
// @param data [Array] data to save
|
|
998
|
+
save(data) {
|
|
892
999
|
return this.storage.data(this.at, this.context.callbacks("beforeSave").call(this.context, data || []));
|
|
893
|
-
}
|
|
1000
|
+
}
|
|
894
1001
|
|
|
895
|
-
|
|
1002
|
+
// load data. It wouldn't load for a second time if it has been loaded.
|
|
1003
|
+
|
|
1004
|
+
// @param data [Array] data to load
|
|
1005
|
+
load(data) {
|
|
896
1006
|
if (!(this.saved() || !data)) {
|
|
897
1007
|
return this._load(data);
|
|
898
1008
|
}
|
|
899
|
-
}
|
|
1009
|
+
}
|
|
900
1010
|
|
|
901
|
-
|
|
1011
|
+
reload(data) {
|
|
902
1012
|
return this._load(data);
|
|
903
|
-
}
|
|
1013
|
+
}
|
|
904
1014
|
|
|
905
|
-
|
|
1015
|
+
// load data from local or remote with callback
|
|
1016
|
+
|
|
1017
|
+
// @param data [Array|String] data to load.
|
|
1018
|
+
_load(data) {
|
|
906
1019
|
if (typeof data === "string") {
|
|
907
1020
|
throw new Error("@gitlab/at.js removed support for $.ajax");
|
|
908
1021
|
} else {
|
|
909
1022
|
return this.save(data);
|
|
910
1023
|
}
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
return Model;
|
|
1024
|
+
}
|
|
914
1025
|
|
|
915
|
-
}
|
|
1026
|
+
};
|
|
916
1027
|
|
|
1028
|
+
// View class to control how At.js's view showing.
|
|
1029
|
+
// All classes share the same DOM view.
|
|
917
1030
|
var View;
|
|
918
1031
|
|
|
919
|
-
View =
|
|
920
|
-
|
|
1032
|
+
View = class View {
|
|
1033
|
+
// @param controller [Object] The Controller.
|
|
1034
|
+
constructor(context) {
|
|
921
1035
|
this.context = context;
|
|
922
1036
|
this.$el = $("<div class='atwho-view'><ul class='atwho-view-ul'></ul></div>");
|
|
923
1037
|
this.$elUl = this.$el.children();
|
|
924
1038
|
this.timeoutID = null;
|
|
1039
|
+
// create HTML DOM of list view if it does not exist
|
|
925
1040
|
this.context.$el.append(this.$el);
|
|
926
1041
|
this.bindEvent();
|
|
927
1042
|
}
|
|
928
1043
|
|
|
929
|
-
|
|
1044
|
+
init() {
|
|
930
1045
|
var header_tpl, id;
|
|
931
1046
|
id = this.context.getOpt("alias") || this.context.at.charCodeAt(0);
|
|
932
1047
|
header_tpl = this.context.getOpt("headerTpl");
|
|
@@ -934,53 +1049,53 @@ View = (function() {
|
|
|
934
1049
|
this.$el.prepend(header_tpl);
|
|
935
1050
|
}
|
|
936
1051
|
return this.$el.attr({
|
|
937
|
-
'id':
|
|
1052
|
+
'id': `at-view-${id}`
|
|
938
1053
|
});
|
|
939
|
-
}
|
|
1054
|
+
}
|
|
940
1055
|
|
|
941
|
-
|
|
1056
|
+
destroy() {
|
|
942
1057
|
return this.$el.remove();
|
|
943
|
-
}
|
|
1058
|
+
}
|
|
944
1059
|
|
|
945
|
-
|
|
1060
|
+
bindEvent() {
|
|
946
1061
|
var $menu, lastCoordX, lastCoordY;
|
|
947
1062
|
$menu = this.$el.find('ul');
|
|
948
1063
|
lastCoordX = 0;
|
|
949
1064
|
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
|
-
};
|
|
1065
|
+
return $menu.on('mousemove.atwho-view', 'li', (e) => {
|
|
1066
|
+
var $cur;
|
|
1067
|
+
// If the mouse hasn't actually moved then exit.
|
|
1068
|
+
if (lastCoordX === e.clientX && lastCoordY === e.clientY) {
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
lastCoordX = e.clientX;
|
|
1072
|
+
lastCoordY = e.clientY;
|
|
1073
|
+
$cur = $(e.currentTarget);
|
|
1074
|
+
if ($cur.hasClass('cur')) {
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
$menu.find('.cur').removeClass('cur');
|
|
1078
|
+
return $cur.addClass('cur');
|
|
1079
|
+
}).on('click.atwho-view', 'li', (e) => {
|
|
1080
|
+
$menu.find('.cur').removeClass('cur');
|
|
1081
|
+
$(e.currentTarget).addClass('cur');
|
|
1082
|
+
this.choose(e);
|
|
1083
|
+
return e.preventDefault();
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
974
1086
|
|
|
975
|
-
|
|
1087
|
+
// Check if view is visible
|
|
1088
|
+
|
|
1089
|
+
// @return [Boolean]
|
|
1090
|
+
visible() {
|
|
976
1091
|
return $.expr.filters.visible(this.$el[0]);
|
|
977
|
-
}
|
|
1092
|
+
}
|
|
978
1093
|
|
|
979
|
-
|
|
1094
|
+
highlighted() {
|
|
980
1095
|
return this.$el.find(".cur").length > 0;
|
|
981
|
-
}
|
|
1096
|
+
}
|
|
982
1097
|
|
|
983
|
-
|
|
1098
|
+
choose(e) {
|
|
984
1099
|
var $li, content;
|
|
985
1100
|
if (($li = this.$el.find(".cur")).length) {
|
|
986
1101
|
content = this.context.insertContentFor($li);
|
|
@@ -992,9 +1107,9 @@ View = (function() {
|
|
|
992
1107
|
if (this.context.getOpt("hideWithoutSuffix")) {
|
|
993
1108
|
return this.stopShowing = true;
|
|
994
1109
|
}
|
|
995
|
-
}
|
|
1110
|
+
}
|
|
996
1111
|
|
|
997
|
-
|
|
1112
|
+
reposition(rect) {
|
|
998
1113
|
var _window, offset, overflowOffset, ref;
|
|
999
1114
|
_window = this.context.app.iframeAsRoot ? this.context.app.window : window;
|
|
1000
1115
|
if (rect.bottom + this.$el.height() - $(_window).scrollTop() > $(_window).height()) {
|
|
@@ -1012,9 +1127,9 @@ View = (function() {
|
|
|
1012
1127
|
}
|
|
1013
1128
|
this.$el.offset(offset);
|
|
1014
1129
|
return this.context.trigger("reposition", [offset]);
|
|
1015
|
-
}
|
|
1130
|
+
}
|
|
1016
1131
|
|
|
1017
|
-
|
|
1132
|
+
next() {
|
|
1018
1133
|
var cur, next, nextEl, offset;
|
|
1019
1134
|
cur = this.$el.find('.cur').removeClass('cur');
|
|
1020
1135
|
next = cur.next();
|
|
@@ -1025,9 +1140,9 @@ View = (function() {
|
|
|
1025
1140
|
nextEl = next[0];
|
|
1026
1141
|
offset = nextEl.offsetTop + nextEl.offsetHeight + (nextEl.nextSibling ? nextEl.nextSibling.offsetHeight : 0);
|
|
1027
1142
|
return this.scrollTop(Math.max(0, offset - this.$el.height()));
|
|
1028
|
-
}
|
|
1143
|
+
}
|
|
1029
1144
|
|
|
1030
|
-
|
|
1145
|
+
prev() {
|
|
1031
1146
|
var cur, offset, prev, prevEl;
|
|
1032
1147
|
cur = this.$el.find('.cur').removeClass('cur');
|
|
1033
1148
|
prev = cur.prev();
|
|
@@ -1038,13 +1153,13 @@ View = (function() {
|
|
|
1038
1153
|
prevEl = prev[0];
|
|
1039
1154
|
offset = prevEl.offsetTop + prevEl.offsetHeight + (prevEl.nextSibling ? prevEl.nextSibling.offsetHeight : 0);
|
|
1040
1155
|
return this.scrollTop(Math.max(0, offset - this.$el.height()));
|
|
1041
|
-
}
|
|
1156
|
+
}
|
|
1042
1157
|
|
|
1043
|
-
|
|
1158
|
+
scrollTop(scrollTop) {
|
|
1044
1159
|
return this.$elUl.scrollTop(scrollTop);
|
|
1045
|
-
}
|
|
1160
|
+
}
|
|
1046
1161
|
|
|
1047
|
-
|
|
1162
|
+
show() {
|
|
1048
1163
|
var rect;
|
|
1049
1164
|
if (this.stopShowing) {
|
|
1050
1165
|
this.stopShowing = false;
|
|
@@ -1058,9 +1173,9 @@ View = (function() {
|
|
|
1058
1173
|
if (rect = this.context.rect()) {
|
|
1059
1174
|
return this.reposition(rect);
|
|
1060
1175
|
}
|
|
1061
|
-
}
|
|
1176
|
+
}
|
|
1062
1177
|
|
|
1063
|
-
|
|
1178
|
+
hide(e, time) {
|
|
1064
1179
|
var callback;
|
|
1065
1180
|
if (!this.visible()) {
|
|
1066
1181
|
return;
|
|
@@ -1069,17 +1184,16 @@ View = (function() {
|
|
|
1069
1184
|
this.$el.hide();
|
|
1070
1185
|
return this.context.trigger('hidden', [e]);
|
|
1071
1186
|
} else {
|
|
1072
|
-
callback = (
|
|
1073
|
-
return
|
|
1074
|
-
|
|
1075
|
-
};
|
|
1076
|
-
})(this);
|
|
1187
|
+
callback = () => {
|
|
1188
|
+
return this.hide();
|
|
1189
|
+
};
|
|
1077
1190
|
clearTimeout(this.timeoutID);
|
|
1078
1191
|
return this.timeoutID = setTimeout(callback, time);
|
|
1079
1192
|
}
|
|
1080
|
-
}
|
|
1193
|
+
}
|
|
1081
1194
|
|
|
1082
|
-
|
|
1195
|
+
// render list view
|
|
1196
|
+
render(list) {
|
|
1083
1197
|
var $li, $ul, i, item, len, li, tpl;
|
|
1084
1198
|
if (!($.isArray(list) && list.length > 0)) {
|
|
1085
1199
|
this.hide();
|
|
@@ -1102,15 +1216,17 @@ View = (function() {
|
|
|
1102
1216
|
if (this.context.getOpt('highlightFirst')) {
|
|
1103
1217
|
return $ul.find("li:first").addClass("cur");
|
|
1104
1218
|
}
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
return View;
|
|
1219
|
+
}
|
|
1108
1220
|
|
|
1109
|
-
}
|
|
1221
|
+
};
|
|
1110
1222
|
|
|
1111
1223
|
var Api;
|
|
1112
1224
|
|
|
1113
1225
|
Api = {
|
|
1226
|
+
// load a flag's data
|
|
1227
|
+
|
|
1228
|
+
// @params at[String] the flag
|
|
1229
|
+
// @params data [Array] data to storage.
|
|
1114
1230
|
load: function(at, data) {
|
|
1115
1231
|
var c;
|
|
1116
1232
|
if (c = this.controller(at)) {
|
|
@@ -1158,7 +1274,7 @@ $.fn.atwho = function(method) {
|
|
|
1158
1274
|
} else if (Api[method] && app) {
|
|
1159
1275
|
return result = Api[method].apply(app, Array.prototype.slice.call(_args, 1));
|
|
1160
1276
|
} else {
|
|
1161
|
-
return $.error(
|
|
1277
|
+
return $.error(`Method ${method} does not exist on jQuery.atwho`);
|
|
1162
1278
|
}
|
|
1163
1279
|
});
|
|
1164
1280
|
if (result != null) {
|
|
@@ -1168,7 +1284,7 @@ $.fn.atwho = function(method) {
|
|
|
1168
1284
|
}
|
|
1169
1285
|
};
|
|
1170
1286
|
|
|
1171
|
-
$.fn.atwho
|
|
1287
|
+
$.fn.atwho.default = {
|
|
1172
1288
|
at: void 0,
|
|
1173
1289
|
alias: void 0,
|
|
1174
1290
|
data: null,
|