diff --git a/src/scripts/controllers/inventory_controller.coffee b/src/scripts/controllers/inventory_controller.coffee
index 345e640b9..669215c70 100644
--- a/src/scripts/controllers/inventory_controller.coffee
+++ b/src/scripts/controllers/inventory_controller.coffee
@@ -9,6 +9,7 @@ BaseController = require './base_controller'
{Duration} = require '../constants'
{Key} = require '../constants'
ImageLoader = require './image_loader'
+NameFinder = require '../models/name_finder'
StackController = require './stack_controller'
########################################################################################################################
@@ -21,10 +22,10 @@ module.exports = class InventoryController extends BaseController
if not options.model? then throw new Error 'options.model is required'
if not options.modPack? then throw new Error 'options.modPack is required'
- options.icon ?= '/images/chest_front.png'
options.editable ?= true
+ options.nameFinder ?= new NameFinder options.modPack
+ options.icon ?= '/images/chest_front.png'
options.imageLoader ?= new ImageLoader defaultUrl:'/images/unknown.png'
- options.gatherNames ?= -> @modPack.gatherNames()
options.title ?= 'Inventory'
options.templateName = 'inventory'
@@ -34,7 +35,7 @@ module.exports = class InventoryController extends BaseController
@icon = options.icon
@imageLoader = options.imageLoader
@modPack = options.modPack
- @gatherNames = options.gatherNames
+ @nameFinder = options.nameFinder
@title = options.title
@_stackControllers = []
@@ -173,7 +174,7 @@ module.exports = class InventoryController extends BaseController
onSelected = => @onItemSelected()
@$nameField.autocomplete
- source: @gatherNames()
+ source: (request, callback)=> callback @nameFinder.search request.term
delay: 0
minLength: 0
change: onChanged
diff --git a/src/scripts/controllers/item_page_controller.coffee b/src/scripts/controllers/item_page_controller.coffee
index b9a8e44c7..7f6932637 100644
--- a/src/scripts/controllers/item_page_controller.coffee
+++ b/src/scripts/controllers/item_page_controller.coffee
@@ -11,6 +11,7 @@ ImageLoader = require './image_loader'
InventoryController = require './inventory_controller'
ItemPage = require '../models/item_page'
ModPackController = require './mod_pack_controller'
+NameFinder = require '../models/name_finder'
########################################################################################################################
@@ -45,7 +46,7 @@ module.exports = class ItemPageController extends BaseController
imageLoader: @imageLoader
model: @model.plan.have
modPack: @model.modPack
- gatherNames: -> @modPack.gatherNames includeGatherable:true
+ nameFinder: new NameFinder @model.modPack, includeGatherable:true
title: 'Items you have'
@needController = @addChild InventoryController, '.need',
@@ -63,7 +64,7 @@ module.exports = class ItemPageController extends BaseController
@modPackController = @addChild ModPackController, '.view__mod_pack', model:@model.modPack
- @$('.want .toolbar').append ' include tools'
@$includeToolsBox = @$('.includeTools')
super
diff --git a/src/scripts/models/mod_pack.coffee b/src/scripts/models/mod_pack.coffee
index 787ab62ae..54dcab29c 100644
--- a/src/scripts/models/mod_pack.coffee
+++ b/src/scripts/models/mod_pack.coffee
@@ -76,21 +76,6 @@ module.exports = class ModPack extends BaseModel
result.itemUrl = Url.item result
return result
- gatherNames: (options={})->
- options.includeGatherable ?= false
- options.includeDisabled ?= false
-
- nameData = {}
- for modVersion in @modVersions
- continue unless modVersion.enabled or options.includeDisabled
- modVersion.gatherNames nameData, options
-
- result = []
- names = _.keys(nameData).sort()
- for name in names
- result.push nameData[name]
- return result
-
hasRecipe: (name, options={})->
options.includeDisabled ?= false
diff --git a/src/scripts/models/mod_version.coffee b/src/scripts/models/mod_version.coffee
index 94b898f21..34c00b73e 100644
--- a/src/scripts/models/mod_version.coffee
+++ b/src/scripts/models/mod_version.coffee
@@ -55,22 +55,6 @@ module.exports = class ModVersion extends BaseModel
findName: (slug)->
return @names[slug]
- gatherNames: (result={}, options={})->
- options.includeGatherable ?= false
-
- for slug, item of @items
- continue if result[item.slug]
- if not item.isCraftable
- continue unless options.includeGatherable
- result[item.slug] = value:item.name, label:"#{item.name} (from #{@name} #{@version})"
-
- if options.includeGatherable
- for slug, name of @names
- continue if result[slug]
- result[slug] = value:name, label:"#{name} (from #{@name} #{@version})"
-
- return result
-
hasRecipe: (name)->
item = @findItemByName name
return false unless item?
diff --git a/src/scripts/models/name_finder.coffee b/src/scripts/models/name_finder.coffee
new file mode 100644
index 000000000..b4ea08051
--- /dev/null
+++ b/src/scripts/models/name_finder.coffee
@@ -0,0 +1,79 @@
+###
+Crafting Guide - name_finder.coffee
+
+Copyright (c) 2015 by Redwood Labs
+All rights reserved.
+###
+
+########################################################################################################################
+
+module.exports = class NameFinder
+
+ constructor: (modPack, options={})->
+ if not modPack? then throw new Error 'modPack is required'
+
+ options.includeGatherable ?= false
+ options.includeDisabledMods ?= false
+ options.limit ?= 25
+
+ @includeDisabledMods = options.includeDisabledMods
+ @includeGatherable = options.includeGatherable
+ @limit = options.limit
+ @modPack = modPack
+ @names = []
+ @_nameMap = {}
+
+ # Public Methods ###############################################################################
+
+ search: (nameHint='')->
+ nameHint = null if nameHint.trim() is ''
+ nameHint = nameHint.toLowerCase() if nameHint?
+ names = @_findNames nameHint
+
+ names.sort (a, b)->
+ c = a.modVersion.compareTo b.modVersion
+ if c isnt 0 then return c
+
+ return 0 if a.label is b.label
+ return if a.label < b.label then -1 else +1
+
+ names = names[0...@limit]
+
+ return names
+
+ # Private Methods ##############################################################################
+
+ _isMatch: (name, hint)->
+ nameWords = name.split ' '
+ hintWords = hint.split ' '
+
+ for hintWord in hintWords
+ while true
+ return false if nameWords.length is 0
+ nameWord = nameWords.shift()
+ break if nameWord.indexOf(hintWord) is 0
+
+ return true
+
+ _findNames: (nameHint=null)->
+ names = []
+ nameMap = {}
+
+ for modVersion in @modPack.modVersions
+ continue unless modVersion.enabled or @includeDisabledMods
+
+ for slug, name of modVersion.names
+ continue if nameMap[name]
+
+ item = modVersion.items[slug]
+ if not @includeGatherable
+ continue unless item? and (not item.isGatherable)
+
+ scanName = "#{modVersion.name} : #{name}"
+ if nameHint?
+ continue unless @_isMatch scanName.toLowerCase(), nameHint
+
+ nameMap[name] = name
+ names.push value:name, label:scanName, modVersion:modVersion
+
+ return names
diff --git a/test/mod_pack.test.coffee b/test/mod_pack.test.coffee
index 2a80ba277..6b72cfe43 100644
--- a/test/mod_pack.test.coffee
+++ b/test/mod_pack.test.coffee
@@ -99,33 +99,6 @@ describe 'ModPack', ->
display.itemName.should.equal 'Iron Chestplate'
display.modSlug.should.equal 'minecraft'
- describe 'gatherNames', ->
-
- it 'finds all registered item names', ->
- buildcraft.enabled = true
- industrialCraft.enabled = true
- (i.value for i in modPack.gatherNames()).sort().should.eql ['Bed', 'Rubber', 'Stone Gear']
-
- it 'ignores duplicate item names', ->
- buildcraft.enabled = true
- names = modPack.gatherNames()
- bedName = (e for e in names when e.value is 'Bed')[0]
- bedName.should.eql value:'Bed', label:'Bed (from Minecraft 1.7.10)'
-
- it 'alphabetizes the item names', ->
- buildcraft.enabled = true
- industrialCraft.enabled = true
- (i.value for i in modPack.gatherNames()).should.eql ['Bed', 'Rubber', 'Stone Gear']
-
- it 'ignores non-craftable items', ->
- (n.value for n in modPack.gatherNames()).sort().should.eql ['Bed']
-
- it 'ignores disabled mod versions', ->
- (n.value for n in modPack.gatherNames()).should.not.include 'Stone Gear'
-
- it "doesn't ignore disabled mod versions when include disabled is requested", ->
- (n.value for n in modPack.gatherNames(includeDisabled:true)).should.include 'Stone Gear'
-
describe 'hasRecipe', ->
it 'returns true when the item is present and has recipes', ->
diff --git a/test/mod_version.test.coffee b/test/mod_version.test.coffee
index ba7c28009..b4fd49b2a 100644
--- a/test/mod_version.test.coffee
+++ b/test/mod_version.test.coffee
@@ -59,27 +59,6 @@ describe 'ModVersion', ->
new Item modVersion:modVersion, name:'Crafting Table'
modVersion.findItemByName('Crafting Table').slug.should.equal 'crafting_table'
- describe 'gatherNames', ->
-
- it 'skips names already found', ->
- new Item modVersion:modVersion, name:'Wool'
- new Item modVersion:modVersion, name:'Oak Wood Planks', recipes:['foo']
- names = modVersion.gatherNames {wool:true}
- names.wool.should.be.true
- names.oak_wood_planks.value.should.equal 'Oak Wood Planks'
-
- it 'only includes craftable items', ->
- new Item modVersion:modVersion, name:'Wool'
- new Item modVersion:modVersion, name:'Oak Wood Planks', recipes:['foo']
- names = modVersion.gatherNames()
- _.keys(names).should.eql ['oak_wood_planks']
-
- it 'computes the proper value and label', ->
- new Item modVersion:modVersion, name:'Oak Wood Planks', recipes:['foo']
- names = modVersion.gatherNames()
- names.oak_wood_planks.value.should.equal 'Oak Wood Planks'
- names.oak_wood_planks.label.should.equal 'Oak Wood Planks (from Test 0.0)'
-
describe 'hasRecipe', ->
it 'returns false for an unknown item', ->