diff --git a/app/assets/javascripts/autocomplete.js b/app/assets/javascripts/autocomplete.js index 7b6274ee8..aaf50081e 100644 --- a/app/assets/javascripts/autocomplete.js +++ b/app/assets/javascripts/autocomplete.js @@ -21,6 +21,9 @@ ); var prefixes = "-|~|general:|gen:|artist:|art:|copyright:|copy:|co:|character:|char:|ch:"; + var metatags = "order|-status|status|-rating|rating|-locked|locked|" + + "-user|user|-approver|approver|commenter|comm|noter|artcomm|-fav|fav|ordfav|" + + "sub|-pool|pool"; $fields_multiple.autocomplete({ delay: 100, @@ -50,7 +53,7 @@ } var term = before_caret_text.match(/\S+/g).pop(); - var regexp = new RegExp("^(?:" + prefixes + ")(.*)$"); + var regexp = new RegExp("^(?:" + prefixes + ")(.*)$", "i"); var match = term.match(regexp); if (match) { term = match[1]; @@ -59,25 +62,55 @@ return; } - $.ajax({ - url: "/tags.json", - data: { - "search[order]": "count", - "search[name_matches]": term + "*", - "limit": 10 - }, - method: "get", - success: function(data) { - resp($.map(data, function(tag) { - return { - label: tag.name.replace(/_/g, " "), - value: tag.name, - category: tag.category, - post_count: tag.post_count - }; - })); - } - }); + regexp = new RegExp("^(" + metatags + "):(.*)$", "i"); + var match = term.match(regexp); + var metatag; + if (match) { + metatag = match[1].toLowerCase(); + term = match[2]; + } + + switch(metatag) { + case "order": + case "status": + case "-status": + case "rating": + case "-rating": + case "locked": + case "-locked": + Danbooru.Autocomplete.static_metatag_source(term, resp, metatag); + break; + } + + if (term === "") { + return; + } + + switch(metatag) { + case "user": + case "-user": + case "approver": + case "-approver": + case "commenter": + case "comm": + case "noter": + case "artcomm": + case "fav": + case "-fav": + case "ordfav": + Danbooru.Autocomplete.user_source(term, resp, metatag); + break; + case "sub": + Danbooru.Autocomplete.subscription_source(term, resp); + break; + case "pool": + case "-pool": + Danbooru.Autocomplete.pool_source(term, resp, metatag); + break; + default: + Danbooru.Autocomplete.normal_source(term, resp); + break; + } } }); @@ -116,27 +149,174 @@ } }); - var render_tag = function(list, tag) { - var $link = $("").addClass("tag-type-" + tag.category).text(tag.label); - $link.attr("href", "/posts?tags=" + encodeURIComponent(tag.value)); - $link.click(function(e) { - e.preventDefault(); - }); + $.merge($fields_multiple, $fields_single).each(function(i, field) { + $(field).data("uiAutocomplete")._renderItem = Danbooru.Autocomplete.render_item; + }); + } + Danbooru.Autocomplete.render_item = function(list, item) { + var $link = $("").text(item.label); + $link.attr("href", "/posts?tags=" + encodeURIComponent(item.value)); + $link.click(function(e) { + e.preventDefault(); + }); + + if (item.post_count !== undefined) { var count; - if (tag.post_count >= 1000) { - count = Math.floor(tag.post_count / 1000) + "k"; + if (item.post_count >= 1000) { + count = Math.floor(item.post_count / 1000) + "k"; } else { - count = tag.post_count; + count = item.post_count; } var $post_count = $("").addClass("post-count").css("float", "right").text(count); $link.append($post_count); + } - return $("
  • ").data("item.autocomplete", tag).append($link).appendTo(list); - }; + 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 (Danbooru.meta("style-usernames") === "true") { + $link.addClass("with-style"); + } + } else if (item.type === "pool") { + $link.addClass("pool-category-" + item.category); + } - $.merge($fields_multiple, $fields_single).each(function(i, field) { - $(field).data("uiAutocomplete")._renderItem = render_tag; + return $("
  • ").data("item.autocomplete", item).append($link).appendTo(list); + }; + + Danbooru.Autocomplete.normal_source = function(term, resp) { + $.ajax({ + url: "/tags.json", + data: { + "search[order]": "count", + "search[name_matches]": term + "*", + "limit": 10 + }, + method: "get", + success: function(data) { + resp($.map(data, function(tag) { + return { + type: "tag", + label: tag.name.replace(/_/g, " "), + value: tag.name, + category: tag.category, + post_count: tag.post_count + }; + })); + } + }); + } + + Danbooru.Autocomplete.static_metatags = { + order: [ + "id", "id_desc", + "score", "score_asc", + "favcount", "favcount_asc", + "comment", "comment_asc", + "note", "note_asc", + "artcomm", "artcomm_asc", + "mpixels", "mpixels_asc", + "portrait", "landscape", + "filesize", "filesize_asc", + "rank" + ], + status: [ + "any", "deleted", "active", "pending", "flagged", "banned" + ], + rating: [ + "safe", "questionable", "explicit" + ], + locked: [ + "rating", "note", "status" + ] + } + + Danbooru.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; + })); + } + + Danbooru.Autocomplete.user_source = function(term, resp, metatag) { + $.ajax({ + url: "/users.json", + data: { + "search[order]": "post_upload_count", + "search[name_matches]": term + "*", + "limit": 10, + }, + method: "get", + success: function(data) { + resp($.map(data, function(user) { + return { + type: "user", + label: user.name.replace(/_/g, " "), + value: metatag + ":" + user.name, + level: user.level_string + }; + })); + } + }); + } + + Danbooru.Autocomplete.subscription_source = function(term, resp) { + var match = term.match(/^(.+?):(.*)$/); + if (match) { + var user_name = match[1]; + var subscription_name = match[2]; + + $.ajax({ + url: "/tag_subscriptions.json", + data: { + "search[creator_name]": user_name, + "search[name_matches]": subscription_name + "*", + "limit": 10 + }, + method: "get", + success: function(data) { + resp($.map(data, function(subscription) { + return { + label: subscription.name.replace(/_/g, " "), + value: "sub:" + user_name + ":" + subscription.name + }; + })); + } + }); + } else { + Danbooru.Autocomplete.user_source(term, resp, "sub"); + } + } + + Danbooru.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 + ":" + pool.name, + post_count: pool.post_count, + category: pool.category + }; + })); + } }); } })(); diff --git a/app/models/tag_subscription.rb b/app/models/tag_subscription.rb index f6bf08b02..2d81da10c 100644 --- a/app/models/tag_subscription.rb +++ b/app/models/tag_subscription.rb @@ -75,6 +75,10 @@ class TagSubscription < ActiveRecord::Base where("creator_id = ?", user.id) end + def name_matches(name) + where("lower(name) like ? escape E'\\\\'", name.to_escaped_for_sql_like) + end + def search(params) q = scoped params = {} if params.blank? @@ -87,6 +91,10 @@ class TagSubscription < ActiveRecord::Base q = q.where("creator_id = ?", CurrentUser.user.id) end + if params[:name_matches] + q = q.name_matches(params[:name_matches].mb_chars.downcase.strip.tr(" ", "_")) + end + q = q.visible_to(CurrentUser.user) q diff --git a/app/views/layouts/default.html.erb b/app/views/layouts/default.html.erb index 4ee0a796c..8237f39ab 100644 --- a/app/views/layouts/default.html.erb +++ b/app/views/layouts/default.html.erb @@ -18,6 +18,7 @@ + <%= auto_discovery_link_tag :atom, posts_path(:format => "atom", :tags => params[:tags]) %> <%= stylesheet_link_tag "application", :media => "screen" %>