├── .gitignore
├── LICENSE
├── mgautocomplete
├── MgAutocompletePlugin.php
├── fieldtypes
│ └── MgAutocomplete_TextAutoCompleteFieldType.php
├── resources
│ ├── css
│ │ └── style.css
│ ├── icon-mask.svg
│ ├── icon.svg
│ └── js
│ │ ├── jquery.ui.autocomplete.js
│ │ └── jquery.ui.deps.js
└── templates
│ └── plaintext.html
├── readme.md
└── releases.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Mildly Geeky, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/mgautocomplete/MgAutocompletePlugin.php:
--------------------------------------------------------------------------------
1 | templates->includeCssResource('mgautocomplete/css/style.css');
14 | craft()->templates->includeJsResource('mgautocomplete/js/jquery.ui.deps.js');
15 | craft()->templates->includeJsResource('mgautocomplete/js/jquery.ui.autocomplete.js');
16 |
17 | return craft()->templates->render('mgautocomplete/plaintext', array(
18 | 'name' => $name,
19 | 'value' => $value,
20 | 'completions' => $this->_getAllCompletions($name, $this->element),
21 | 'settings' => $this->getSettings()
22 | ));
23 | }
24 |
25 | protected function _getAllCompletions($name, $element)
26 | {
27 | $completions = array();
28 |
29 | $field = craft()->fields->getFieldByHandle($name);
30 | if ($field == null or !$field->hasContentColumn()) {
31 | return $completions;
32 | }
33 |
34 | $contentTable = craft()->content->contentTable;
35 |
36 | $fieldCol = 'field_' . $name;
37 |
38 | $query = craft()->db->createCommand()
39 | ->selectDistinct($fieldCol)
40 | ->from($contentTable)
41 | ->where(array(
42 | 'locale' => $element->getAttribute('locale')
43 | ))
44 | ->andWhere($fieldCol . ' is not null')
45 | ->queryAll();
46 |
47 | for ($i = 0; $i < count($query); $i++) {
48 | array_push($completions, $query[$i][$fieldCol]);
49 | }
50 |
51 | return $completions;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/mgautocomplete/resources/css/style.css:
--------------------------------------------------------------------------------
1 | .mgautocomplete__ul {
2 | margin-top: -3px;
3 | background: #fff;
4 | border: none;
5 | border-radius: 0 0 2px 2px;
6 | box-shadow: 0 0 2px rgba(51, 170, 255, 0), inset 0 0 0 1px rgba(0, 0, 0, 0.1), inset 0 2px 4px rgba(0, 0, 0, 0.1), inset 0 0 0 1px rgba(51, 170, 255, 0);
7 | }
8 |
9 | .mgautocomplete__ul li {
10 | padding: 6px 7px;
11 | transition: all 100ms cubic-bezier(0.420, 0.000, 0.580, 1.000); /* ease-in-out */
12 | }
13 |
14 | .mgautocomplete__ul li:last-child {
15 | border-bottom-left-radius: 2px;
16 | border-bottom-right-radius: 2px;
17 | }
18 |
19 | .mgautocomplete__ul li.ui-state-focus {
20 | background: #7dafe8;
21 | color: #fff;
22 | }
23 |
24 | .ui-helper-hidden {
25 | display: none;
26 | }
27 |
28 | .ui-helper-hidden-accessible {
29 | border: 0;
30 | clip: rect(0 0 0 0);
31 | height: 1px;
32 | margin: -1px;
33 | overflow: hidden;
34 | padding: 0;
35 | position: absolute;
36 | width: 1px;
37 | }
38 |
39 | .ui-helper-reset {
40 | margin: 0;
41 | padding: 0;
42 | border: 0;
43 | outline: 0;
44 | line-height: 1.3;
45 | text-decoration: none;
46 | font-size: 100%;
47 | list-style: none;
48 | }
49 |
50 | .ui-helper-clearfix:before,
51 | .ui-helper-clearfix:after {
52 | content: "";
53 | display: table;
54 | border-collapse: collapse;
55 | }
56 |
57 | .ui-helper-clearfix:after {
58 | clear: both;
59 | }
60 |
61 | .ui-helper-clearfix {
62 | min-height: 0; /* support: IE7 */
63 | }
64 |
65 | .ui-helper-zfix {
66 | width: 100%;
67 | height: 100%;
68 | top: 0;
69 | left: 0;
70 | position: absolute;
71 | opacity: 0;
72 | filter: Alpha(Opacity=0); /* support: IE8 */
73 | }
74 |
75 | .ui-front {
76 | z-index: 100;
77 | }
78 |
79 | /* Interaction Cues
80 | ----------------------------------*/
81 | .ui-state-disabled {
82 | cursor: default !important;
83 | }
84 |
85 | /* Icons
86 | ----------------------------------*/
87 |
88 | /* states and images */
89 | .ui-icon {
90 | display: block;
91 | text-indent: -99999px;
92 | overflow: hidden;
93 | background-repeat: no-repeat;
94 | }
95 |
96 | /* Misc visuals
97 | ----------------------------------*/
98 |
99 | /* Overlays */
100 | .ui-widget-overlay {
101 | position: fixed;
102 | top: 0;
103 | left: 0;
104 | width: 100%;
105 | height: 100%;
106 | }
107 |
108 | .ui-autocomplete {
109 | position: absolute;
110 | top: 0;
111 | left: 0;
112 | cursor: default;
113 | }
114 |
115 | .ui-menu {
116 | list-style: none;
117 | padding: 0;
118 | margin: 0;
119 | display: block;
120 | outline: none;
121 | }
122 |
123 | .ui-menu .ui-menu {
124 | position: absolute;
125 | }
126 |
127 | .ui-menu .ui-menu-item {
128 | position: relative;
129 | margin: 0;
130 | padding: 3px 1em 3px .4em;
131 | cursor: pointer;
132 | min-height: 0; /* support: IE7 */
133 | /* support: IE10, see #8844 */
134 | list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
135 | }
136 |
137 | .ui-menu .ui-menu-divider {
138 | margin: 5px 0;
139 | height: 0;
140 | font-size: 0;
141 | line-height: 0;
142 | border-width: 1px 0 0 0;
143 | }
144 |
145 | .ui-menu .ui-state-focus,
146 | .ui-menu .ui-state-active {
147 | margin: -1px;
148 | }
149 |
150 | /* icon support */
151 | .ui-menu-icons {
152 | position: relative;
153 | }
154 |
155 | .ui-menu-icons .ui-menu-item {
156 | padding-left: 2em;
157 | }
158 |
159 | /* left-aligned */
160 | .ui-menu .ui-icon {
161 | position: absolute;
162 | top: 0;
163 | bottom: 0;
164 | left: .2em;
165 | margin: auto 0;
166 | }
167 |
168 | /* right-aligned */
169 | .ui-menu .ui-menu-icon {
170 | left: auto;
171 | right: 0;
172 | }
--------------------------------------------------------------------------------
/mgautocomplete/resources/icon-mask.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/mgautocomplete/resources/icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/mgautocomplete/resources/js/jquery.ui.autocomplete.js:
--------------------------------------------------------------------------------
1 | /*! jQuery UI - v1.11.4 - 2015-11-05
2 | * http://jqueryui.com
3 | * Includes: core.js, widget.js, position.js, autocomplete.js, menu.js
4 | * Copyright jQuery Foundation and other contributors; Licensed MIT */
5 |
6 | (function( factory ) {
7 | if ( typeof define === "function" && define.amd ) {
8 |
9 | // AMD. Register as an anonymous module.
10 | define([ "jquery" ], factory );
11 | } else {
12 |
13 | // Browser globals
14 | factory( jQuery );
15 | }
16 | }(function( $ ) {
17 |
18 | /*!
19 | * jQuery UI Autocomplete 1.12.1
20 | * http://jqueryui.com
21 | *
22 | * Copyright jQuery Foundation and other contributors
23 | * Released under the MIT license.
24 | * http://jquery.org/license
25 | */
26 |
27 | //>>label: Autocomplete
28 | //>>group: Widgets
29 | //>>description: Lists suggested words as the user is typing.
30 | //>>docs: http://api.jqueryui.com/autocomplete/
31 | //>>demos: http://jqueryui.com/autocomplete/
32 | //>>css.structure: ../../themes/base/core.css
33 | //>>css.structure: ../../themes/base/autocomplete.css
34 | //>>css.theme: ../../themes/base/theme.css
35 |
36 | $.widget( "ui.autocomplete", {
37 | version: "1.12.1",
38 | defaultElement: "",
39 | options: {
40 | appendTo: null,
41 | autoFocus: false,
42 | delay: 300,
43 | minLength: 1,
44 | position: {
45 | my: "left top",
46 | at: "left bottom",
47 | collision: "none"
48 | },
49 | source: null,
50 |
51 | // Callbacks
52 | change: null,
53 | close: null,
54 | focus: null,
55 | open: null,
56 | response: null,
57 | search: null,
58 | select: null
59 | },
60 |
61 | requestIndex: 0,
62 | pending: 0,
63 |
64 | _create: function() {
65 |
66 | // Some browsers only repeat keydown events, not keypress events,
67 | // so we use the suppressKeyPress flag to determine if we've already
68 | // handled the keydown event. #7269
69 | // Unfortunately the code for & in keypress is the same as the up arrow,
70 | // so we use the suppressKeyPressRepeat flag to avoid handling keypress
71 | // events when we know the keydown event was used to modify the
72 | // search term. #7799
73 | var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
74 | nodeName = this.element[ 0 ].nodeName.toLowerCase(),
75 | isTextarea = nodeName === "textarea",
76 | isInput = nodeName === "input";
77 |
78 | // Textareas are always multi-line
79 | // Inputs are always single-line, even if inside a contentEditable element
80 | // IE also treats inputs as contentEditable
81 | // All other element types are determined by whether or not they're contentEditable
82 | this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element );
83 |
84 | this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
85 | this.isNewMenu = true;
86 |
87 | this._addClass( "ui-autocomplete-input" );
88 | this.element.attr( "autocomplete", "off" );
89 |
90 |
91 | this._on( this.element, {
92 | keydown: function( event ) {
93 | if ( this.element.prop( "readOnly" ) ) {
94 | suppressKeyPress = true;
95 | suppressInput = true;
96 | suppressKeyPressRepeat = true;
97 | return;
98 | }
99 |
100 | suppressKeyPress = false;
101 | suppressInput = false;
102 | suppressKeyPressRepeat = false;
103 | var keyCode = $.ui.keyCode;
104 | switch ( event.keyCode ) {
105 | case keyCode.PAGE_UP:
106 | suppressKeyPress = true;
107 | this._move( "previousPage", event );
108 | break;
109 | case keyCode.PAGE_DOWN:
110 | suppressKeyPress = true;
111 | this._move( "nextPage", event );
112 | break;
113 | case keyCode.UP:
114 | suppressKeyPress = true;
115 | this._keyEvent( "previous", event );
116 | break;
117 | case keyCode.DOWN:
118 | suppressKeyPress = true;
119 | this._keyEvent( "next", event );
120 | break;
121 | case keyCode.ENTER:
122 |
123 | // when menu is open and has focus
124 | if ( this.menu.active ) {
125 |
126 | // #6055 - Opera still allows the keypress to occur
127 | // which causes forms to submit
128 | suppressKeyPress = true;
129 | event.preventDefault();
130 | this.menu.select( event );
131 | }
132 | break;
133 | case keyCode.TAB:
134 | if ( this.menu.active ) {
135 | this.menu.select( event );
136 | }
137 | break;
138 | case keyCode.ESCAPE:
139 | if ( this.menu.element.is( ":visible" ) ) {
140 | if ( !this.isMultiLine ) {
141 | this._value( this.term );
142 | }
143 | this.close( event );
144 |
145 | // Different browsers have different default behavior for escape
146 | // Single press can mean undo or clear
147 | // Double press in IE means clear the whole form
148 | event.preventDefault();
149 | }
150 | break;
151 | default:
152 | suppressKeyPressRepeat = true;
153 |
154 | // search timeout should be triggered before the input value is changed
155 | this._searchTimeout( event );
156 | break;
157 | }
158 | },
159 | keypress: function( event ) {
160 | if ( suppressKeyPress ) {
161 | suppressKeyPress = false;
162 | if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
163 | event.preventDefault();
164 | }
165 | return;
166 | }
167 | if ( suppressKeyPressRepeat ) {
168 | return;
169 | }
170 |
171 | // Replicate some key handlers to allow them to repeat in Firefox and Opera
172 | var keyCode = $.ui.keyCode;
173 | switch ( event.keyCode ) {
174 | case keyCode.PAGE_UP:
175 | this._move( "previousPage", event );
176 | break;
177 | case keyCode.PAGE_DOWN:
178 | this._move( "nextPage", event );
179 | break;
180 | case keyCode.UP:
181 | this._keyEvent( "previous", event );
182 | break;
183 | case keyCode.DOWN:
184 | this._keyEvent( "next", event );
185 | break;
186 | }
187 | },
188 | input: function( event ) {
189 | if ( suppressInput ) {
190 | suppressInput = false;
191 | event.preventDefault();
192 | return;
193 | }
194 | this._searchTimeout( event );
195 | },
196 | focus: function() {
197 | this.selectedItem = null;
198 | this.previous = this._value();
199 | },
200 | blur: function( event ) {
201 | if ( this.cancelBlur ) {
202 | delete this.cancelBlur;
203 | return;
204 | }
205 |
206 | clearTimeout( this.searching );
207 | this.close( event );
208 | this._change( event );
209 | }
210 | } );
211 |
212 | this._initSource();
213 | this.menu = $( "
" )
214 | .appendTo( this._appendTo() )
215 | .menu( {
216 |
217 | // disable ARIA support, the live region takes care of that
218 | role: null
219 | } )
220 | .hide()
221 | .menu( "instance" );
222 |
223 | this._addClass( this.menu.element, "ui-autocomplete", "ui-front" );
224 | this._on( this.menu.element, {
225 | mousedown: function( event ) {
226 |
227 | // prevent moving focus out of the text field
228 | event.preventDefault();
229 |
230 | // IE doesn't prevent moving focus even with event.preventDefault()
231 | // so we set a flag to know when we should ignore the blur event
232 | this.cancelBlur = true;
233 | this._delay( function() {
234 | delete this.cancelBlur;
235 |
236 | // Support: IE 8 only
237 | // Right clicking a menu item or selecting text from the menu items will
238 | // result in focus moving out of the input. However, we've already received
239 | // and ignored the blur event because of the cancelBlur flag set above. So
240 | // we restore focus to ensure that the menu closes properly based on the user's
241 | // next actions.
242 | if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
243 | this.element.trigger( "focus" );
244 | }
245 | } );
246 | },
247 | menufocus: function( event, ui ) {
248 | var label, item;
249 |
250 | // support: Firefox
251 | // Prevent accidental activation of menu items in Firefox (#7024 #9118)
252 | if ( this.isNewMenu ) {
253 | this.isNewMenu = false;
254 | if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
255 | this.menu.blur();
256 |
257 | this.document.one( "mousemove", function() {
258 | $( event.target ).trigger( event.originalEvent );
259 | } );
260 |
261 | return;
262 | }
263 | }
264 |
265 | item = ui.item.data( "ui-autocomplete-item" );
266 | if ( false !== this._trigger( "focus", event, { item: item } ) ) {
267 |
268 | // use value to match what will end up in the input, if it was a key event
269 | if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
270 | this._value( item.value );
271 | }
272 | }
273 |
274 | // Announce the value in the liveRegion
275 | label = ui.item.attr( "aria-label" ) || item.value;
276 | if ( label && $.trim( label ).length ) {
277 | this.liveRegion.children().hide();
278 | $( "" ).text( label ).appendTo( this.liveRegion );
279 | }
280 | },
281 | menuselect: function( event, ui ) {
282 | var item = ui.item.data( "ui-autocomplete-item" ),
283 | previous = this.previous;
284 |
285 | // Only trigger when focus was lost (click on menu)
286 | if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
287 | this.element.trigger( "focus" );
288 | this.previous = previous;
289 |
290 | // #6109 - IE triggers two focus events and the second
291 | // is asynchronous, so we need to reset the previous
292 | // term synchronously and asynchronously :-(
293 | this._delay( function() {
294 | this.previous = previous;
295 | this.selectedItem = item;
296 | } );
297 | }
298 |
299 | if ( false !== this._trigger( "select", event, { item: item } ) ) {
300 | this._value( item.value );
301 | }
302 |
303 | // reset the term after the select event
304 | // this allows custom select handling to work properly
305 | this.term = this._value();
306 |
307 | this.close( event );
308 | this.selectedItem = item;
309 | }
310 | } );
311 |
312 | this.liveRegion = $( "
", {
313 | role: "status",
314 | "aria-live": "assertive",
315 | "aria-relevant": "additions"
316 | } )
317 | .appendTo( this.document[ 0 ].body );
318 |
319 | this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
320 |
321 | // Turning off autocomplete prevents the browser from remembering the
322 | // value when navigating through history, so we re-enable autocomplete
323 | // if the page is unloaded before the widget is destroyed. #7790
324 | this._on( this.window, {
325 | beforeunload: function() {
326 | this.element.removeAttr( "autocomplete" );
327 | }
328 | } );
329 | },
330 |
331 | _destroy: function() {
332 | clearTimeout( this.searching );
333 | this.element.removeAttr( "autocomplete" );
334 | this.menu.element.remove();
335 | this.liveRegion.remove();
336 | },
337 |
338 | _setOption: function( key, value ) {
339 | this._super( key, value );
340 | if ( key === "source" ) {
341 | this._initSource();
342 | }
343 | if ( key === "appendTo" ) {
344 | this.menu.element.appendTo( this._appendTo() );
345 | }
346 | if ( key === "disabled" && value && this.xhr ) {
347 | this.xhr.abort();
348 | }
349 | },
350 |
351 | _isEventTargetInWidget: function( event ) {
352 | var menuElement = this.menu.element[ 0 ];
353 |
354 | return event.target === this.element[ 0 ] ||
355 | event.target === menuElement ||
356 | $.contains( menuElement, event.target );
357 | },
358 |
359 | _closeOnClickOutside: function( event ) {
360 | if ( !this._isEventTargetInWidget( event ) ) {
361 | this.close();
362 | }
363 | },
364 |
365 | _appendTo: function() {
366 | var element = this.options.appendTo;
367 |
368 | if ( element ) {
369 | element = element.jquery || element.nodeType ?
370 | $( element ) :
371 | this.document.find( element ).eq( 0 );
372 | }
373 |
374 | if ( !element || !element[ 0 ] ) {
375 | element = this.element.closest( ".ui-front, dialog" );
376 | }
377 |
378 | if ( !element.length ) {
379 | element = this.document[ 0 ].body;
380 | }
381 |
382 | return element;
383 | },
384 |
385 | _initSource: function() {
386 | var array, url,
387 | that = this;
388 | if ( $.isArray( this.options.source ) ) {
389 | array = this.options.source;
390 | this.source = function( request, response ) {
391 | response( $.ui.autocomplete.filter( array, request.term ) );
392 | };
393 | } else if ( typeof this.options.source === "string" ) {
394 | url = this.options.source;
395 | this.source = function( request, response ) {
396 | if ( that.xhr ) {
397 | that.xhr.abort();
398 | }
399 | that.xhr = $.ajax( {
400 | url: url,
401 | data: request,
402 | dataType: "json",
403 | success: function( data ) {
404 | response( data );
405 | },
406 | error: function() {
407 | response( [] );
408 | }
409 | } );
410 | };
411 | } else {
412 | this.source = this.options.source;
413 | }
414 | },
415 |
416 | _searchTimeout: function( event ) {
417 | clearTimeout( this.searching );
418 | this.searching = this._delay( function() {
419 |
420 | // Search if the value has changed, or if the user retypes the same value (see #7434)
421 | var equalValues = this.term === this._value(),
422 | menuVisible = this.menu.element.is( ":visible" ),
423 | modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
424 |
425 | if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) {
426 | this.selectedItem = null;
427 | this.search( null, event );
428 | }
429 | }, this.options.delay );
430 | },
431 |
432 | search: function( value, event ) {
433 | value = value != null ? value : this._value();
434 |
435 | // Always save the actual value, not the one passed as an argument
436 | this.term = this._value();
437 |
438 | if ( value.length < this.options.minLength ) {
439 | return this.close( event );
440 | }
441 |
442 | if ( this._trigger( "search", event ) === false ) {
443 | return;
444 | }
445 |
446 | return this._search( value );
447 | },
448 |
449 | _search: function( value ) {
450 | this.pending++;
451 | this._addClass( "ui-autocomplete-loading" );
452 | this.cancelSearch = false;
453 |
454 | this.source( { term: value }, this._response() );
455 | },
456 |
457 | _response: function() {
458 | var index = ++this.requestIndex;
459 |
460 | return $.proxy( function( content ) {
461 | if ( index === this.requestIndex ) {
462 | this.__response( content );
463 | }
464 |
465 | this.pending--;
466 | if ( !this.pending ) {
467 | this._removeClass( "ui-autocomplete-loading" );
468 | }
469 | }, this );
470 | },
471 |
472 | __response: function( content ) {
473 | if ( content ) {
474 | content = this._normalize( content );
475 | }
476 | this._trigger( "response", null, { content: content } );
477 | if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
478 | this._suggest( content );
479 | this._trigger( "open" );
480 | } else {
481 |
482 | // use ._close() instead of .close() so we don't cancel future searches
483 | this._close();
484 | }
485 | },
486 |
487 | close: function( event ) {
488 | this.cancelSearch = true;
489 | this._close( event );
490 | },
491 |
492 | _close: function( event ) {
493 |
494 | // Remove the handler that closes the menu on outside clicks
495 | this._off( this.document, "mousedown" );
496 |
497 | if ( this.menu.element.is( ":visible" ) ) {
498 | this.menu.element.hide();
499 | this.menu.blur();
500 | this.isNewMenu = true;
501 | this._trigger( "close", event );
502 | }
503 | },
504 |
505 | _change: function( event ) {
506 | if ( this.previous !== this._value() ) {
507 | this._trigger( "change", event, { item: this.selectedItem } );
508 | }
509 | },
510 |
511 | _normalize: function( items ) {
512 |
513 | // assume all items have the right format when the first item is complete
514 | if ( items.length && items[ 0 ].label && items[ 0 ].value ) {
515 | return items;
516 | }
517 | return $.map( items, function( item ) {
518 | if ( typeof item === "string" ) {
519 | return {
520 | label: item,
521 | value: item
522 | };
523 | }
524 | return $.extend( {}, item, {
525 | label: item.label || item.value,
526 | value: item.value || item.label
527 | } );
528 | } );
529 | },
530 |
531 | _suggest: function( items ) {
532 | var ul = this.menu.element.empty();
533 | this._renderMenu( ul, items );
534 | this.isNewMenu = true;
535 | this.menu.refresh();
536 |
537 | // Size and position menu
538 | ul.show();
539 | this._resizeMenu();
540 | ul.position( $.extend( {
541 | of: this.element
542 | }, this.options.position ) );
543 |
544 | if ( this.options.autoFocus ) {
545 | this.menu.next();
546 | }
547 |
548 | // Listen for interactions outside of the widget (#6642)
549 | this._on( this.document, {
550 | mousedown: "_closeOnClickOutside"
551 | } );
552 | },
553 |
554 | _resizeMenu: function() {
555 | var ul = this.menu.element;
556 | ul.outerWidth( Math.max(
557 |
558 | // Firefox wraps long text (possibly a rounding bug)
559 | // so we add 1px to avoid the wrapping (#7513)
560 | ul.width( "" ).outerWidth() + 1,
561 | this.element.outerWidth()
562 | ) );
563 | },
564 |
565 | _renderMenu: function( ul, items ) {
566 | var that = this;
567 | $.each( items, function( index, item ) {
568 | that._renderItemData( ul, item );
569 | } );
570 | },
571 |
572 | _renderItemData: function( ul, item ) {
573 | return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
574 | },
575 |
576 | _renderItem: function( ul, item ) {
577 | return $( "
- " )
578 | .append( $( "
" ).text( item.label ) )
579 | .appendTo( ul );
580 | },
581 |
582 | _move: function( direction, event ) {
583 | if ( !this.menu.element.is( ":visible" ) ) {
584 | this.search( null, event );
585 | return;
586 | }
587 | if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
588 | this.menu.isLastItem() && /^next/.test( direction ) ) {
589 |
590 | if ( !this.isMultiLine ) {
591 | this._value( this.term );
592 | }
593 |
594 | this.menu.blur();
595 | return;
596 | }
597 | this.menu[ direction ]( event );
598 | },
599 |
600 | widget: function() {
601 | return this.menu.element;
602 | },
603 |
604 | _value: function() {
605 | return this.valueMethod.apply( this.element, arguments );
606 | },
607 |
608 | _keyEvent: function( keyEvent, event ) {
609 | if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
610 | this._move( keyEvent, event );
611 |
612 | // Prevents moving cursor to beginning/end of the text field in some browsers
613 | event.preventDefault();
614 | }
615 | },
616 |
617 | // Support: Chrome <=50
618 | // We should be able to just use this.element.prop( "isContentEditable" )
619 | // but hidden elements always report false in Chrome.
620 | // https://code.google.com/p/chromium/issues/detail?id=313082
621 | _isContentEditable: function( element ) {
622 | if ( !element.length ) {
623 | return false;
624 | }
625 |
626 | var editable = element.prop( "contentEditable" );
627 |
628 | if ( editable === "inherit" ) {
629 | return this._isContentEditable( element.parent() );
630 | }
631 |
632 | return editable === "true";
633 | }
634 | } );
635 |
636 | $.extend( $.ui.autocomplete, {
637 | escapeRegex: function( value ) {
638 | return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
639 | },
640 | filter: function( array, term ) {
641 | var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" );
642 | return $.grep( array, function( value ) {
643 | return matcher.test( value.label || value.value || value );
644 | } );
645 | }
646 | } );
647 |
648 | // Live region extension, adding a `messages` option
649 | // NOTE: This is an experimental API. We are still investigating
650 | // a full solution for string manipulation and internationalization.
651 | $.widget( "ui.autocomplete", $.ui.autocomplete, {
652 | options: {
653 | messages: {
654 | noResults: "No search results.",
655 | results: function( amount ) {
656 | return amount + ( amount > 1 ? " results are" : " result is" ) +
657 | " available, use up and down arrow keys to navigate.";
658 | }
659 | }
660 | },
661 |
662 | __response: function( content ) {
663 | var message;
664 | this._superApply( arguments );
665 | if ( this.options.disabled || this.cancelSearch ) {
666 | return;
667 | }
668 | if ( content && content.length ) {
669 | message = this.options.messages.results( content.length );
670 | } else {
671 | message = this.options.messages.noResults;
672 | }
673 | this.liveRegion.children().hide();
674 | $( "
" ).text( message ).appendTo( this.liveRegion );
675 | }
676 | } );
677 |
678 | var widgetsAutocomplete = $.ui.autocomplete;
679 |
680 | }));
--------------------------------------------------------------------------------
/mgautocomplete/resources/js/jquery.ui.deps.js:
--------------------------------------------------------------------------------
1 | (function( factory ) {
2 | if ( typeof define === "function" && define.amd ) {
3 |
4 | // AMD. Register as an anonymous module.
5 | define([ "jquery" ], factory );
6 | } else {
7 |
8 | // Browser globals
9 | factory( jQuery );
10 | }
11 | }(function( $ ) {
12 |
13 | /*!
14 | * jQuery UI Unique ID 1.12.1
15 | * http://jqueryui.com
16 | *
17 | * Copyright jQuery Foundation and other contributors
18 | * Released under the MIT license.
19 | * http://jquery.org/license
20 | */
21 |
22 | //>>label: uniqueId
23 | //>>group: Core
24 | //>>description: Functions to generate and remove uniqueId's
25 | //>>docs: http://api.jqueryui.com/uniqueId/
26 |
27 | var uniqueId = $.fn.extend( {
28 | uniqueId: ( function() {
29 | var uuid = 0;
30 |
31 | return function() {
32 | return this.each( function() {
33 | if ( !this.id ) {
34 | this.id = "ui-id-" + ( ++uuid );
35 | }
36 | } );
37 | };
38 | } )(),
39 |
40 | removeUniqueId: function() {
41 | return this.each( function() {
42 | if ( /^ui-id-\d+$/.test( this.id ) ) {
43 | $( this ).removeAttr( "id" );
44 | }
45 | } );
46 | }
47 | } );
48 |
49 | var safeActiveElement = $.ui.safeActiveElement = function( document ) {
50 | var activeElement;
51 |
52 | // Support: IE 9 only
53 | // IE9 throws an "Unspecified error" accessing document.activeElement from an