diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 1ae4f6c8b..cda277c2c 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -22,6 +22,7 @@ module.exports = (grunt)-> extensions: ['.coffee'] files: './dist/js/main.js': ['./src/coffee/main.coffee'] + test: options: transform: ['coffeeify'] @@ -30,6 +31,15 @@ module.exports = (grunt)-> extensions: ['.coffee'] files: './dist/js/test.js': ['./src/coffee/test/test.coffee'] + + markdown: + options: + browserifyOptions: + debug: false + standalone: 'markdown' + files: + './dist/js/markdown.js': ['./node_modules/markdown/lib/index.js'] + when: options: browserifyOptions: @@ -102,6 +112,12 @@ module.exports = (grunt)-> src: './static/' dest: './dist/' recursive: true + non_html: + options: + src: './static/' + dest: './dist/' + exclude: '*.html' + recursive: true sass: build: @@ -134,7 +150,7 @@ module.exports = (grunt)-> tasks: ['browserify:main'] jade: files: ['./src/**/*.jade'] - tasks: ['jade:pages', 'jade:templates'] + tasks: ['jade:pages', 'jade:templates', 'browserify:main'] sass: files: ['./src/**/*.scss'] tasks: ['sass', 'copy:styles'] @@ -144,8 +160,8 @@ module.exports = (grunt)-> grunt.registerTask 'default', 'build' - grunt.registerTask 'build', [ 'rsync', 'copy', 'sass:build', 'jade', 'browserify', 'exorcise' ] + grunt.registerTask 'build', [ 'rsync:non_html', 'copy', 'sass:build', 'jade', 'browserify', 'exorcise' ] - grunt.registerTask 'dist', ['clean', 'build', 'copy:index_prerender', 'sass:dist', 'uglify'] + grunt.registerTask 'dist', ['clean', 'build', 'rsync:static', 'copy:index_prerender', 'sass:dist', 'uglify'] grunt.registerTask 'clean-watch', ['clean', 'build', 'watch'] diff --git a/package.json b/package.json index e05b1095e..be459bc64 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "chai": "^1.10.0", "jade": "^1.8.2", "jquery": "^2.1.3", + "markdown": "^0.5.0", "mocha": "^2.0.1", "sinon": "^1.12.2", "sinon-chai": "^2.6.0", diff --git a/src/coffee/constants.coffee b/src/coffee/constants.coffee index 7faac4801..f69892e5e 100644 --- a/src/coffee/constants.coffee +++ b/src/coffee/constants.coffee @@ -56,14 +56,15 @@ ModelState.failed = 'failed' exports.Text = Text = {} Text.title = 'Crafting Guide for Minecraft | The Ultimate Step-by-Step Tutorial for Making Anything in Minecraft' -exports.Url = Url = {} -Url.crafting = _.template "/craft/<%= inventoryText %>" -Url.itemIcon = _.template "/browse/<%= modSlug %>/<%= itemSlug %>/icon.png" -Url.item = _.template "/browse/<%= modSlug %>/<%= itemSlug %>/" -Url.mod = _.template "/browse/<%= modSlug %>/" -Url.modData = _.template "/data/<%= modSlug %>/mod.cg" -Url.modIcon = _.template "/browse/<%= modSlug %>/icon.png" -Url.modVersion = _.template "/data/<%= modSlug %>/<%= modVersion %>/mod-version.cg" +exports.Url = Url = {} +Url.crafting = _.template "/craft/<%= inventoryText %>" +Url.item = _.template "/browse/<%= modSlug %>/<%= itemSlug %>/" +Url.itemData = _.template "/browse/<%= modSlug %>/<%= itemSlug %>/item.cg" +Url.itemIcon = _.template "/browse/<%= modSlug %>/<%= itemSlug %>/icon.png" +Url.mod = _.template "/browse/<%= modSlug %>/" +Url.modData = _.template "/data/<%= modSlug %>/mod.cg" +Url.modIcon = _.template "/browse/<%= modSlug %>/icon.png" +Url.modVersionData = _.template "/data/<%= modSlug %>/<%= modVersion %>/mod-version.cg" exports.UrlParam = UrlParam = {} UrlParam.quantity = 'count' diff --git a/src/coffee/controllers/item_controller.coffee b/src/coffee/controllers/item_controller.coffee index 4cc196d26..e05056db6 100644 --- a/src/coffee/controllers/item_controller.coffee +++ b/src/coffee/controllers/item_controller.coffee @@ -18,8 +18,8 @@ module.exports = class ItemController extends BaseController options.templateName = 'item' super options - @_imageLoader = options.imageLoader - @_modPack = options.modPack + @imageLoader = options.imageLoader + @modPack = options.modPack # BaseController Overrides ##################################################################### @@ -30,9 +30,9 @@ module.exports = class ItemController extends BaseController super refresh: -> - display = @_modPack.findItemDisplay @model.slug + display = @modPack.findItemDisplay @model.slug - @_imageLoader.load display.iconUrl, @$icon + @imageLoader.load display.iconUrl, @$icon @$name.html display.itemName @$nameLink.attr 'href', display.itemUrl diff --git a/src/coffee/controllers/item_group_controller.coffee b/src/coffee/controllers/item_group_controller.coffee index e0383506d..52450b8a7 100644 --- a/src/coffee/controllers/item_group_controller.coffee +++ b/src/coffee/controllers/item_group_controller.coffee @@ -76,7 +76,6 @@ module.exports = class ItemGroupController extends BaseController delay += @_delayStep else controller.model = item - controller.refresh() controllerIndex += 1 while @_itemControllers.length > controllerIndex diff --git a/src/coffee/controllers/item_page_controller.coffee b/src/coffee/controllers/item_page_controller.coffee index e0de87e89..030872a94 100644 --- a/src/coffee/controllers/item_page_controller.coffee +++ b/src/coffee/controllers/item_page_controller.coffee @@ -57,15 +57,17 @@ module.exports = class ItemPageController extends PageController imageLoader: @imageLoader modPack: @modPack - @$usedAsToolToMakeSection = @$('.usedAsToolToMake') @$byline = @$('.byline') @$bylineLink = @$('.byline a') @$craftingPlanLink = @$('a.craftingPlan') + @$descriptionPanel = @$('.description .panel') + @$descriptionSection = @$('.description') @$name = @$('h1.name') @$recipeContainer = @$('.recipes .panel') @$recipesSection = @$('.recipes') @$similarSection = @$('.similar') @$titleImage = @$('.titleImage img') + @$usedAsToolToMakeSection = @$('.usedAsToolToMake') @$usedToMakeSection = @$('.usedToMake') super @@ -84,6 +86,7 @@ module.exports = class ItemPageController extends PageController @$el.slideUp duration:Duration.normal @_refreshByline() + @_refreshDescription() @_refreshRecipes() @_refreshSimilarItems() @_refreshUsedAsToolToMake() @@ -100,15 +103,6 @@ module.exports = class ItemPageController extends PageController # Private Methods ############################################################################## - _refreshUsedAsToolToMake: -> - @_usedAsToolToMakeController.title = 'Used as Tool to Make' - - @_usedAsToolToMakeController.model = @model.findToolForRecipes() - if @_usedAsToolToMakeController.model? - @$usedAsToolToMakeSection.slideDown duration:Duration.normal - else - @$usedAsToolToMakeSection.slideUp duration:Duration.normal - _refreshByline: -> mod = @model.item?.modVersion?.mod if mod?.name?.length > 0 @@ -118,14 +112,15 @@ module.exports = class ItemPageController extends PageController else @$byline.fadeOut duration:Duration.fast - _refreshUsedToMake: -> - @_usedToMakeController.title = 'Used to Make' - - @_usedToMakeController.model = @model.findComponentInItems() - if @_usedToMakeController.model? - @$usedToMakeSection.slideDown duration:Duration.normal + _refreshDescription: -> + logger.debug "refreshing description" + logger.debug "item.description: #{@model?.item?.description}" + description = @model.compileDescription() + if description? + @$descriptionPanel.html description + @$descriptionSection.slideDown duration:Duration.normal else - @$usedToMakeSection.slideUp duration:Duration.normal + @$descriptionSection.slideUp duration:Duration.normal _refreshRecipes: -> @_recipeControllers ?= [] @@ -165,10 +160,35 @@ module.exports = class ItemPageController extends PageController else @$similarSection.slideUp duration:Duration.normal - _resolveItemSlug: -> - item = @modPack.findItem @_itemSlug, includeDisabled:false - if item? and not ItemSlug.equal item.slug, @_itemSlug - router.navigate Url.item(modSlug:item.slug.mod, itemSlug:item.slug.item), trigger:true - return + _refreshUsedAsToolToMake: -> + @_usedAsToolToMakeController.title = 'Used as Tool to Make' - @model.item = item + @_usedAsToolToMakeController.model = @model.findToolForRecipes() + if @_usedAsToolToMakeController.model? + @$usedAsToolToMakeSection.slideDown duration:Duration.normal + else + @$usedAsToolToMakeSection.slideUp duration:Duration.normal + + _refreshUsedToMake: -> + @_usedToMakeController.title = 'Used to Make' + + @_usedToMakeController.model = @model.findComponentInItems() + if @_usedToMakeController.model? + @$usedToMakeSection.slideDown duration:Duration.normal + else + @$usedToMakeSection.slideUp duration:Duration.normal + + _resolveItemSlug: -> + return if @model.item? + + item = @modPack.findItem @_itemSlug, includeDisabled:false + if item? + if not ItemSlug.equal item.slug, @_itemSlug + router.navigate Url.item(modSlug:item.slug.mod, itemSlug:item.slug.item), trigger:true + return + + @model.item = item + item.fetch() + item.on Event.sync, => + logger.debug "item loaded" + @refresh() diff --git a/src/coffee/models/item.coffee b/src/coffee/models/item.coffee index ad77fe109..ee0cdf387 100644 --- a/src/coffee/models/item.coffee +++ b/src/coffee/models/item.coffee @@ -10,6 +10,7 @@ BaseModel = require './base_model' ItemSlug = require './item_slug' Recipe = require './recipe' StringBuilder = require './string_builder' +{Url} = require '../constants' ######################################################################################################################## @@ -57,6 +58,18 @@ module.exports = class Item extends BaseModel Object.defineProperties @prototype, isCraftable: {get:@prototype.getIsCraftable} + # Backbone.Model Overrides ##################################################################### + + parse: (text)-> + ItemParser = require './item_parser' # to avoid require cycles + @_parser ?= new ItemParser model:this + @_parser.parse text + + return null # prevent calling `set` + + url: -> + return Url.itemData modSlug:@slug.mod, itemSlug:@slug.item + # Object Overrides ############################################################################# toString: -> diff --git a/src/coffee/models/item_page.coffee b/src/coffee/models/item_page.coffee index c6885b6fe..264976e94 100644 --- a/src/coffee/models/item_page.coffee +++ b/src/coffee/models/item_page.coffee @@ -20,6 +20,10 @@ module.exports = class ItemPage extends BaseModel # Property Methods ############################################################################# + compileDescription: -> + return null unless @item?.description? + return markdown.parse @item.description + findComponentInItems: -> return @_findRecipesMatching (recipe)=> recipe.requires @item.slug diff --git a/src/coffee/models/item_parser.coffee b/src/coffee/models/item_parser.coffee new file mode 100644 index 000000000..f3cc5be07 --- /dev/null +++ b/src/coffee/models/item_parser.coffee @@ -0,0 +1,19 @@ +### +Crafting Guide - item_parser.coffee + +Copyright (c) 2014-2015 by Redwood Labs +All rights reserved. +### + +VersionedParserBase = require './versioned_parser_base' +ItemParserV1 = require './parser_versions/item_parser_v1' + +######################################################################################################################## + +module.exports = class ItemParser extends VersionedParserBase + + # VersionedParserBase Overrides ################################################################ + + _createParsers: (options)-> + return result = + '1': new ItemParserV1 options diff --git a/src/coffee/models/mod_version.coffee b/src/coffee/models/mod_version.coffee index e4acb95eb..3dd5bd298 100644 --- a/src/coffee/models/mod_version.coffee +++ b/src/coffee/models/mod_version.coffee @@ -171,7 +171,7 @@ module.exports = class ModVersion extends BaseModel return null # prevent calling `set` url: -> - return Url.modVersion modSlug:@modSlug, modVersion:@version + return Url.modVersionData modSlug:@modSlug, modVersion:@version # Object Overrides ############################################################################# diff --git a/src/coffee/models/parser_versions/command_parser_version_base.coffee b/src/coffee/models/parser_versions/command_parser_version_base.coffee index c286b1fda..c9b29050d 100644 --- a/src/coffee/models/parser_versions/command_parser_version_base.coffee +++ b/src/coffee/models/parser_versions/command_parser_version_base.coffee @@ -78,7 +78,7 @@ module.exports = class CommandParserVersionBase line = line.trim() return [] if line.length is 0 - hereDoc = @_parseHereDoc line + [line, hereDoc] = @_parseHereDoc line lineParts = (part.trim() for part in line.split(';')) commands = [] @@ -89,8 +89,10 @@ module.exports = class CommandParserVersionBase if not match? then throw new Error "Expected : , but found: \"#{linePart}\"" args = [] - args = (s.trim() for s in match[2].split(',')) if match[2]? - args.push hereDoc if hereDoc + args = (s for s in match[2].split(',') when s.length > 0) if match[2]? + args = (s.trim() for s in args) + args = (s for s in args when s.length > 0) + args.push hereDoc if hereDoc? commands.push name:match[1], args:args return commands @@ -107,9 +109,11 @@ module.exports = class CommandParserVersionBase _parseHereDoc: (line)-> hereDocIndex = line.indexOf '<<-' - return null unless hereDocIndex isnt -1 + return [line, null] unless hereDocIndex isnt -1 hereDocStopText = line[hereDocIndex+3...line.length] + line = line[0...hereDocIndex] + hereDocLines = [] while true @_lineNumber += 1 @@ -120,11 +124,11 @@ module.exports = class CommandParserVersionBase hereDocLines.push nextLine shortestIndent = Number.MAX_VALUE - for line in hereDocLines - shortestIndent = Math.min line.match(/( *).*/)[1].length, shortestIndent + for hereDocLine in hereDocLines + shortestIndent = Math.min hereDocLine.match(/( *).*/)[1].length, shortestIndent for i in [0...hereDocLines.length] hereDocLines[i] = hereDocLines[i][shortestIndent..] - return null unless hereDocLines.length > 0 - return hereDocLines.join '\n' + return [line, null] unless hereDocLines.length > 0 + return [line, hereDocLines.join('\n')] diff --git a/src/coffee/models/parser_versions/item_parser_v1.coffee b/src/coffee/models/parser_versions/item_parser_v1.coffee index b658df195..251bce482 100644 --- a/src/coffee/models/parser_versions/item_parser_v1.coffee +++ b/src/coffee/models/parser_versions/item_parser_v1.coffee @@ -22,16 +22,17 @@ module.exports = class ItemParserV1 extends CommandParserVersionBase # Command Methods ############################################################################## _command_description: (textParts...)-> - @_rawData.text ?= '' - @_rawData.text += textParts.join ', ' + @_rawData.description ?= '' + @_rawData.description += textParts.join ', ' _command_officialUrl: (officialUrl)-> if @_rawData.officialUrl? then throw new Error 'duplicate declaration of "officialUrl"' if officialUrl.length is 0 then throw new Error 'officialUrl cannot be empty' @_rawData.officialUrl = officialUrl - _command_video: (youTubeId, name)-> + _command_video: (youTubeId, nameParts...)-> if not youTubeId?.length then throw new Error 'video declaration requires a YouTubeID' + name = nameParts.join ', ' if not name?.length then throw new Error 'video declaration requires a name' @_rawData.videos ?= [] @@ -40,6 +41,6 @@ module.exports = class ItemParserV1 extends CommandParserVersionBase # Object Building Methods ###################################################################### _buildItem: (rawData, model)-> - item.description = rawData.description if rawData.description - item.officialUrl = rawData.officialUrl if rawData.officialUrl - item.videos = rawData.videos if rawData.videos + model.description = rawData.description if rawData.description + model.officialUrl = rawData.officialUrl if rawData.officialUrl + model.videos = rawData.videos if rawData.videos diff --git a/src/coffee/test/parser_versions/command_parser_version_base.test.coffee b/src/coffee/test/parser_versions/command_parser_version_base.test.coffee index 48e09df7c..6cfe148a7 100644 --- a/src/coffee/test/parser_versions/command_parser_version_base.test.coffee +++ b/src/coffee/test/parser_versions/command_parser_version_base.test.coffee @@ -21,25 +21,28 @@ describe 'command_parser_version_base.coffee', -> it 'returns null for non-heredoc lines', -> result = parser._parseHereDoc 'foobar: baz' - expect(result).to.be.null + expect(result[1]).to.be.null it 'identifies the right text for a real heredoc', -> parser._lines = ['command: <<-END', 'alpha', 'bravo', 'charlie', 'END', 'command1: arg2'] parser._lineNumber = 1 result = parser._parseHereDoc parser._lines[0] - result.should.equal 'alpha\nbravo\ncharlie' + result[0].should.equal 'command: ' + result[1].should.equal 'alpha\nbravo\ncharlie' it 'identifies an empty heredoc', -> parser._lines = ['command: <<-END', 'END'] parser._lineNumber = 1 result = parser._parseHereDoc parser._lines[0] - expect(result).to.be.null + result[0].should.equal 'command: ' + expect(result[1]).to.be.null it 'trims smallest leading whitespace', -> parser._lines = ['command: <<-END', ' alpha', ' bravo', ' charlie', 'END', 'command1: arg2'] parser._lineNumber = 1 result = parser._parseHereDoc parser._lines[0] - result.should.equal 'alpha\n bravo\ncharlie' + result[0].should.equal 'command: ' + result[1].should.equal 'alpha\n bravo\ncharlie' diff --git a/src/coffee/views.js b/src/coffee/views.js index 976afc0aa..11389a338 100644 --- a/src/coffee/views.js +++ b/src/coffee/views.js @@ -103,7 +103,7 @@ var buf = []; var jade_mixins = {}; var jade_interp; -buf.push("

See Crafting Plan

\n\n\n\n

from

Recipes

");;return buf.join(""); +buf.push("

See Crafting Plan

\n\n\n\n

from

Description

Recipes

");;return buf.join(""); }; this["JST"]["minimal_recipe"] = function template(locals) { diff --git a/src/jade/layouts/basic.jade b/src/jade/layouts/basic.jade index 1f685b817..610864710 100644 --- a/src/jade/layouts/basic.jade +++ b/src/jade/layouts/basic.jade @@ -34,4 +34,5 @@ html script(src="/js/jquery.js") script(src="/js/backbone.js") script(src="/js/jade.js") + script(src="/js/markdown.js") script(src="/js/when.js") diff --git a/src/jade/templates/item_page.jade b/src/jade/templates/item_page.jade index e782fd9e6..753e58349 100644 --- a/src/jade/templates/item_page.jade +++ b/src/jade/templates/item_page.jade @@ -15,7 +15,10 @@ .mainBody h1.name .byline: p from - .description: p + + .description.section + h2 Description + .panel.markdown .recipes.section h2 Recipes diff --git a/src/scss/main.scss b/src/scss/main.scss index 5cd269330..02618d70c 100644 --- a/src/scss/main.scss +++ b/src/scss/main.scss @@ -86,6 +86,7 @@ $layer-dialog: 1000; // Child Stylesheets /////////////////////////////////////////////////////////////////////////////////////////////////// @import 'classes'; +@import 'markdown'; @import 'tags'; @import 'views'; diff --git a/src/scss/markdown.scss b/src/scss/markdown.scss new file mode 100644 index 000000000..01cd3fd7e --- /dev/null +++ b/src/scss/markdown.scss @@ -0,0 +1,22 @@ +/* +Crafting Guide - markdown.scss + +Copyright (C) 2015 by Redwood Labs +All rights reserved. +*/ + +.markdown { + + p { + font-family: $font-family-normal; + font-size: $font-size-normal; + line-height: $font-size-normal + 0.25em; + } + + img { + position: relative; left: 50%; + @include transform(translateX(-50%)); + margin: 2em 0; + text-align: center; + } +} diff --git a/src/scss/templates/item_page.scss b/src/scss/templates/item_page.scss index cfb35e203..5903b096c 100644 --- a/src/scss/templates/item_page.scss +++ b/src/scss/templates/item_page.scss @@ -31,15 +31,6 @@ All rights reserved. } } - .description { - margin-bottom: 4em; - - p { - font-family: $font-family-normal; - font-size: $font-size-large; - } - } - .recipes { display: none; diff --git a/static/browse/buildcraft/advanced_crafting_table/gui.png b/static/browse/buildcraft/advanced_crafting_table/gui.png new file mode 100644 index 000000000..1001ce3d2 Binary files /dev/null and b/static/browse/buildcraft/advanced_crafting_table/gui.png differ diff --git a/static/browse/buildcraft/advanced_crafting_table/item.cg b/static/browse/buildcraft/advanced_crafting_table/item.cg new file mode 100644 index 000000000..769b5fb41 --- /dev/null +++ b/static/browse/buildcraft/advanced_crafting_table/item.cg @@ -0,0 +1,15 @@ +schema: 1 + +officialUrl: http://www.mod-buildcraft.com/wiki/doku.php?id=advanced_crafting_table + +video: Z-Kl3IRjxE, Tutorial + +description:<<-END +The advanced crafting table is an improved version of the basic automatic crafting table. It contains an inventory +for storing the input and an inventory for storing the outputs of the crafting process, making it potentially more +easy and seamless to create large automatic crafting systems. The advanced crafting table requires 500 MJ to craft +any recipe. + +![GUI](./gui.png) + +END