import Utility from './utility' import SavedSearch from './saved_searches' let Autocomplete = {}; Autocomplete.AUTOCOMPLETE_VERSION = 1; Autocomplete.PREFIXES = /^(-|~|<%= TagCategory.mapping.keys.map {|category| category + ':'}.join('|') %>)(.*)$/i; Autocomplete.METATAGS = /^(<%= Tag::METATAGS %>):(.*)$/i; Autocomplete.initialize_all = function() { if (Utility.meta("enable-auto-complete") === "true") { $.widget("ui.autocomplete", $.ui.autocomplete, { options: { delay: 0, minLength: 1, autoFocus: false, focus: function() { return false; }, }, _create: function() { this.element.on("keydown.Autocomplete.tab", null, "tab", Autocomplete.on_tab); this._super(); }, _renderItem: Autocomplete.render_item, }); this.initialize_tag_autocomplete(); this.initialize_mention_autocomplete($(".autocomplete-mentions textarea")); this.initialize_artist_autocomplete($('[data-autocomplete="artist"]')); this.initialize_pool_autocomplete($('[data-autocomplete="pool"]')); this.initialize_wiki_autocomplete($('[data-autocomplete="wiki-page"]')); } } Autocomplete.initialize_mention_autocomplete = function($fields) { $fields.autocomplete({ search: function() { $(this).data("ui-autocomplete").menu.bindings = $(); }, select: function(event, ui) { Autocomplete.insert_completion(this, ui.item.value); return false; }, source: function(req, resp) { var cursor = this.element.get(0).selectionStart; var i; var name = null; for (i=cursor; i>=1; --i) { if (req.term[i-1] === " ") { return; } if (req.term[i-1] === "@") { if (i == 1 || /[ \r\n]/.test(req.term[i-2])) { name = req.term.substring(i, cursor); break; } else { return; } } } if (name) { Autocomplete.user_source(name, resp, "@"); } return; } }); } Autocomplete.initialize_tag_autocomplete = function() { var $fields_multiple = $('[data-autocomplete="tag-query"], [data-autocomplete="tag-edit"]'); var $fields_single = $('[data-autocomplete="tag"]'); $fields_multiple.autocomplete({ search: function() { if ($(this).data("ui-autocomplete")) { $(this).data("ui-autocomplete").menu.bindings = $(); } }, select: function(event, ui) { // Prevent Upload.initialize_enter_on_tags from running if the // Enter key is used to select a tag from the autocomplete menu. if (event.key === "Enter") { event.stopImmediatePropagation(); } Autocomplete.insert_completion(this, ui.item.value); return false; }, source: function(req, resp) { var query = Autocomplete.parse_query(req.term, this.element.get(0).selectionStart); var metatag = query.metatag; var term = query.term; if (!metatag && !term) { this.close(); return; } switch(metatag) { case "md5": case "width": case "height": case "mpixels": case "ratio": case "score": case "favcount": case "filesize": case "source": case "id": case "date": case "age": case "limit": case "tagcount": case "pixiv_id": case "pixiv": <% TagCategory.short_name_list.each do |category| %> case "<%= category %>tags": <% end %> resp([]); return; case "order": case "status": case "rating": case "locked": case "child": case "parent": case "filetype": Autocomplete.static_metatag_source(term, resp, metatag); return; case "user": case "approver": case "commenter": case "comm": case "noter": case "noteupdater": case "artcomm": case "fav": case "ordfav": case "appealer": case "flagger": case "upvote": case "downvote": Autocomplete.user_source(term, resp, metatag); break; case "pool": case "ordpool": Autocomplete.pool_source(term, resp, metatag); break; case "favgroup": Autocomplete.favorite_group_source(term, resp, metatag); break; case "search": Autocomplete.saved_search_source(term, resp); break; default: Autocomplete.normal_source(term, resp); break; } } }); $fields_single.autocomplete({ search: function() { $(this).data("ui-autocomplete").menu.bindings = $(); }, source: function(req, resp) { Autocomplete.normal_source(req.term, resp); } }); } Autocomplete.initialize_artist_autocomplete = function($fields) { $fields.autocomplete({ search: function() { $(this).data("ui-autocomplete").menu.bindings = $(); }, source: function(req, resp) { $.ajax({ url: "/artists.json", data: { "search[name]": req.term + "*", "search[is_active]": true, "search[order]": "post_count", "limit": 10, "expiry": 7 }, method: "get", success: function(data) { resp($.map(data, function(artist) { return { type: "tag", label: artist.name.replace(/_/g, " "), value: artist.name, category: <%= Tag.categories.artist %>, }; })); } }); } }); }; Autocomplete.initialize_pool_autocomplete = function($fields) { $fields.autocomplete({ search: function() { $(this).data("ui-autocomplete").menu.bindings = $(); }, source: function(req, resp) { Autocomplete.pool_source(req.term, resp); }, }); }; Autocomplete.initialize_wiki_autocomplete = function($fields) { $fields.autocomplete({ search: function() { $(this).data("ui-autocomplete").menu.bindings = $(); }, source: function(req, resp) { $.ajax({ url: "/wiki_pages.json", data: { "search[title]": req.term + "*", "search[hide_deleted]": "Yes", "search[order]": "post_count", "limit": 10, "expiry": 7 }, method: "get", success: function(data) { resp($.map(data, function(wiki_page) { return { type: "tag", label: wiki_page.title.replace(/_/g, " "), value: wiki_page.title, category: wiki_page.category_name }; })); } }); } }); }; Autocomplete.normal_source = function(term, resp) { var key = "ac-" + term.replace(/\./g,'\uFFFF'); $.ajax({ url: "/tags/autocomplete.json", data: { "search[name_matches]": term, "expiry": 7 }, method: "get", success: function(data) { var d = $.map(data, function(tag) { return { type: "tag", label: tag.name.replace(/_/g, " "), antecedent: tag.antecedent_name, value: tag.name, category: tag.category, post_count: tag.post_count }; }); resp(d); } }); } Autocomplete.parse_query = function(text, caret) { var metatag = ""; var term = ""; var before_caret_text = text.substring(0, caret); var match = before_caret_text.match(/\S+$/g); if (match) { term = match[0]; } else { return {}; } if (match = term.match(Autocomplete.PREFIXES)) { metatag = match[1].toLowerCase(); term = match[2]; } if (match = term.match(Autocomplete.METATAGS)) { metatag = match[1].toLowerCase(); term = match[2]; } return { metatag: metatag, term: term }; }; // Update the input field with the item currently focused in the // autocomplete menu, then position the caret just after the inserted completion. Autocomplete.insert_completion = function(input, completion) { var before_caret_text = input.value.substring(0, input.selectionStart).trim(); var after_caret_text = input.value.substring(input.selectionStart).trim(); var prefixes = "-|~|" + "<%= TagCategory.mapping.keys.map {|category| category + ':'}.join('|') %>"; var regexp = new RegExp("(" + prefixes + ")?\\S+$", "g"); before_caret_text = before_caret_text.replace(regexp, "$1") + completion + " "; input.value = before_caret_text + after_caret_text; input.selectionStart = input.selectionEnd = before_caret_text.length; }; // If we press tab while the autocomplete menu is open but nothing is // focused, complete the first item and close the menu. Autocomplete.on_tab = function(event) { var input = this; var autocomplete = $(input).autocomplete("instance"); var $autocomplete_menu = autocomplete.menu.element; if (!$autocomplete_menu.is(":visible")) { return; } if ($autocomplete_menu.has(".ui-state-active").length === 0) { var $first_item = $autocomplete_menu.find(".ui-menu-item").first(); var completion = $first_item.data().uiAutocompleteItem.value; Autocomplete.insert_completion(input, completion); autocomplete.close(); } // Prevent the tab key from moving focus to the next element. event.preventDefault(); }; Autocomplete.render_item = function(list, item) { var $link = $(""); $link.text(item.label); $link.attr("href", "/posts?tags=" + encodeURIComponent(item.value)); $link.click(function(e) { e.preventDefault(); }); if (item.antecedent) { var antecedent = item.antecedent.replace(/_/g, " "); var arrow = $("").html(" → ").addClass("autocomplete-arrow"); var antecedent_element = $("").text(antecedent).addClass("autocomplete-antecedent"); $link.prepend([ antecedent_element, arrow ]); } if (item.post_count !== undefined) { var count; if (item.post_count >= 1000) { count = Math.floor(item.post_count / 1000) + "k"; } else { count = item.post_count; } var $post_count = $("").addClass("post-count").css("float", "right").text(count); $link.append($post_count); } if (item.type === "tag") { $link.addClass("tag-type-" + item.category); } else if (item.type === "user") { var level_class = "user-" + item.level.toLowerCase(); $link.addClass(level_class); if (Utility.meta("style-usernames") === "true") { $link.addClass("with-style"); } } else if (item.type === "pool") { $link.addClass("pool-category-" + item.category); } var $menu_item = $("
").append($link); return $("
  • ").data("item.autocomplete", item).append($menu_item).appendTo(list); }; Autocomplete.static_metatags = { order: [ "id", "id_desc", "score", "score_asc", "favcount", "favcount_asc", "created_at", "created_at_asc", "change", "change_asc", "comment", "comment_asc", "comment_bumped", "comment_bumped_asc", "note", "note_asc", "artcomm", "artcomm_asc", "mpixels", "mpixels_asc", "portrait", "landscape", "filesize", "filesize_asc", "tagcount", "tagcount_asc", "rank", "random", "custom" ].concat(<%= TagCategory.short_name_list.map {|category| [category + "tags", category + "tags_asc"]}.flatten %>), status: [ "any", "deleted", "active", "pending", "flagged", "banned" ], rating: [ "safe", "questionable", "explicit" ], locked: [ "rating", "note", "status" ], child: [ "any", "none" ], parent: [ "any", "none" ], filetype: [ "jpg", "png", "gif", "swf", "zip", "webm", "mp4" ], } Autocomplete.static_metatag_source = function(term, resp, metatag) { var sub_metatags = this.static_metatags[metatag]; var regexp = new RegExp("^" + $.ui.autocomplete.escapeRegex(term), "i"); var matches = $.grep(sub_metatags, function (sub_metatag) { return regexp.test(sub_metatag); }); resp($.map(matches, function(sub_metatag) { return metatag + ":" + sub_metatag; })); } Autocomplete.user_source = function(term, resp, metatag) { $.ajax({ url: "/users.json", data: { "search[order]": "post_upload_count", "search[current_user_first]": "true", "search[name_matches]": term + "*", "limit": 10 }, method: "get", success: function(data) { var prefix; var display_name; if (metatag === "@") { prefix = "@"; display_name = function(name) {return name;}; } else { prefix = metatag + ":"; display_name = function(name) {return name.replace(/_/g, " ");}; } resp($.map(data, function(user) { return { type: "user", label: display_name(user.name), value: prefix + user.name, level: user.level_string }; })); } }); } Autocomplete.pool_source = function(term, resp, metatag) { $.ajax({ url: "/pools.json", data: { "search[order]": "post_count", "search[name_matches]": term, "limit": 10 }, method: "get", success: function(data) { resp($.map(data, function(pool) { return { type: "pool", label: pool.name.replace(/_/g, " "), value: (metatag ? (metatag + ":" + pool.name) : pool.name), post_count: pool.post_count, category: pool.category }; })); } }); } Autocomplete.favorite_group_source = function(term, resp, metatag) { $.ajax({ url: "/favorite_groups.json", data: { "search[name_matches]": term, "limit": 10 }, method: "get", success: function(data) { resp($.map(data, function(favgroup) { return { label: favgroup.name.replace(/_/g, " "), value: metatag + ":" + favgroup.name, post_count: favgroup.post_count }; })); } }); } Autocomplete.saved_search_source = function(term, resp) { return SavedSearch.labels(term).then(function(labels) { resp(labels.map(function(label) { return { label: label.replace(/_/g, " "), value: "search:" + label, }; })); }); } $(document).ready(function() { Autocomplete.initialize_all(); });