Skip to content

Instantly share code, notes, and snippets.

@htatche
Last active August 29, 2015 13:55
Show Gist options
  • Save htatche/8690253 to your computer and use it in GitHub Desktop.
Save htatche/8690253 to your computer and use it in GitHub Desktop.
Modifiers

Ember View for Modifiers

Ember View created with performance in mind, instead of using any template library, the DOM code is injected directly from the javascript.

Loading this screen of the app takes aprox 100ms even on slowest computers/mobiles.

ModifiersPopup = Ember.View.extend
tagName: "div"
templateName: "menus/modifiers_popup"
product: null
related_categories: []
unrelated_categories: []
unrelated_cats_options:
per_page: 11
viewport: [0, 11]
selected_modifiers: []
selected_category: null
mode: "add"
### HTML Templates ###
htmlOfCategories: (categories) ->
html = ""
categories.forEach (category) => html = html + @htmlOfCategory(category)
return html
htmlOfCategory: (category) ->
category_id = parseInt(category.get('id'))
html = ""
return html if category.get("modifiers").get("length") == 0
html = html +
"""
<div class='modifier-category' data-category-id=#{category.get('id')}>
<div class='header'>
<div class='title left'> #{category.get('name')} </div>
<div class='requirements left'>
#{@htmlOfModifierCategoryRequirements category_id}
</div>
</div>
<div class='items'>
"""
category.get("modifiers_not_deleted").forEach (modifier) =>
html = html + @htmlOfModifier(modifier)
html = html +
"""
</div>
</div>
"""
return html
htmlOfModifier: (modifier) ->
html = ""
html = html +
"""
<a class='modifier-button' data-modifier-id=#{modifier.get('id')}>
<div class='wrapper'>
<div class='text'>
<div class='title'> #{modifier.get('visible_name')} </div>
"""
unless modifier.get("price") == 0
html = html +
"""
<div class='price'> #{currency modifier.get('price')} </div>
"""
html = html +
"""
</div>
</div>
</a>
"""
return html
htmlOfFollowOnModifierCategory: (follow_on_modifier_cat, parent_modifier) ->
html = ""
html = html +
"""
<div class='follow-on-modifier-category'
data-category-id=#{follow_on_modifier_cat.get('id')}>
<div class='header'>
<div class='title left'>
#{parent_modifier.get('visible_name')}
requires
#{follow_on_modifier_cat.get('name')}
</div>
</div>
<div class='items'>
"""
follow_on_modifier_cat.get("modifiers").forEach (modifier) =>
html = html + @htmlOfModifier(modifier)
html = html +
"""
</div>
</div>
"""
return html
htmlOfUnrelatedCategories: (viewport) ->
html = ""
html = html +
"""
<a class='unrelated-cat-btn active'
data-action='preset'
data-unrelated-category-id='preset'>
<div class='wrapper'>
<div class='text'>
Preset
</div>
</div>
</a>
"""
@get("unrelated_categories")
.slice(viewport[0], viewport[1])
.forEach (category) =>
html = html +
"""
<a class='unrelated-cat-btn'
data-unrelated-category-id=#{category.get('id')} >
<div class='wrapper'>
<div class='text'>
#{category.get('name')}
</div>
</div>
</a>
"""
return html
htmlOfModifierCategoryRequirements: (category_id) ->
max = @get("product").maximumModifierChoices(category_id)
min = @get("product").minimumModifierChoices(category_id)
html = ""
html = html + " Maximum of " + max unless max is 0 or !max?
html = html + " Minimum of " + min unless min is 0 or !min?
return html
### Handlers ###
willInsertElement: ->
@set "mode", "add"
@set "unrelated_cats_options.viewport", [0, 11]
@set "selected_category", "preset"
@set "selected_modifiers", []
@set "product", null
didInsertElement: ->
@set "product", @get("controller.active_modifiers.product")
@set "related_categories",
@get("product").modifierCategories()
@set "unrelated_categories",
@get("product").unrelatedModifierCategories @get("related_categories")
@prepareTemplate()
# Load previously selected modifiers if product is not new in the till roll
@loadSelectedModifiers() unless @get("controller.active_modifiers.new_product")
touchStart: (event) ->
@performAction(event) if window.isTouchDevice
mouseUp: (event) ->
@performAction(event) if !window.isTouchDevice
performAction: (event) ->
el = $(event.target).closest("a")
# Back / Done
@backButtonPressed() if el.data("action") is "back"
@doneButtonPressed() if el.data("action") is "save"
# Add Note
@addNoteButtonPressed() if el.data("action") is "add-note"
# Mode
@addModeButtonPressed(el) if el.data("action") is "add"
@dontAddModeButtonPressed(el) if el.data("action") is "dont-add"
# Category related
@presetButtonPressed() if el.data("action") is "preset"
@modifierButtonPressed(el) if el.data("modifier-id")
@categoryButtonPressed(id) if id = el.data("unrelated-category-id")
# Category list
@previousPageButtonPressed(el) if el.data("action") is "previous-page"
@nextPageButtonPressed(el) if el.data("action") is "next-page"
backButtonPressed: -> @close()
doneButtonPressed: -> @save()
addNoteButtonPressed: ->
@save()
@get("controller.controllers.more_menu").addNoteButtonPressed()
addModeButtonPressed: (el) ->
unless @get("mode") == "add"
$(".add-btn").toggleClass "selected"
$(".dont-add-btn").toggleClass "selected"
@set "mode", "add"
dontAddModeButtonPressed: (el) ->
unless @get("mode") == "dont_add"
$(".add-btn").toggleClass "selected"
$(".dont-add-btn").toggleClass "selected"
@set "mode", "dont_add"
modifierButtonPressed: (el) ->
status = el.data("status")
modifier = DS.Modifier.find(el.data("modifier-id"))
category = modifier.get("modifier_category")
max_reached = @aboveMaxModifiersInCategory(category)
# This is not working !!!!!
return if @get("mode") == "add" and max_reached and status != @get("mode")
switch status
when undefined
@selectModifier modifier, @get("mode")
when @get("mode")
@unselectModifier modifier
else
@changeModifier modifier, @switchStatus(status)
presetButtonPressed: ->
@categoryButtonPressed "preset"
categoryButtonPressed: (category_id) ->
if @get("selected_category") == "preset"
previous = "preset"
else
previous = @get("selected_category.id")
$("[data-unrelated-category-id=#{previous}]")
.removeClass "active"
$("[data-unrelated-category-id=#{category_id}]")
.addClass "active"
@selectCategory category_id
@showSelectedModifiers()
previousPageButtonPressed: (el) ->
viewport = @get "unrelated_cats_options.viewport"
per_page = @get "unrelated_cats_options.per_page"
if viewport[0] > 0
viewport = [viewport[0] - per_page, viewport[0]]
document
.getElementById("unrelated-categories-list-html")
.innerHTML = @htmlOfUnrelatedCategories(viewport)
@set "unrelated_cats_options.viewport", viewport
nextPageButtonPressed: (el) ->
viewport = @get "unrelated_cats_options.viewport"
per_page = @get "unrelated_cats_options.per_page"
if viewport[1] < @get("unrelated_categories.length")
viewport = [viewport[1], viewport[1] + per_page]
document
.getElementById("unrelated-categories-list-html")
.innerHTML = @htmlOfUnrelatedCategories(viewport)
@set "unrelated_cats_options.viewport", viewport
### Logic ###
prepareTemplate: ->
document
.getElementById("title")
.innerHTML = "Modify #{@get('product.name')}"
document
.getElementById("modifiers-list-html")
.innerHTML = @htmlOfCategories(@get("related_categories"))
document
.getElementById("unrelated-categories-list-html")
.innerHTML = @htmlOfUnrelatedCategories(@get("unrelated_cats_options.viewport"))
# Show unrelated categories controls if list is long
max_viewport = this.get("unrelated_cats_options.viewport.lastObject")
if @get("unrelated_categories.length") > max_viewport
$("a.prev-next-button .wrapper").show()
loadSelectedModifiers: ->
line_item = @get("controller.controllers.order.selectedLineItem")
return unless line_item?
@set "selected_modifiers", []
line_item.get("line_item_modifiers").toArray().forEach (lim) =>
@selectModifier lim.get("modifier"), lim.get("status")
selectModifier: (modifier, status) ->
follow_on_modifier_cat = modifier.followOnModifierCategory()
id = modifier.get("id")
$("[data-modifier-id=#{id}]")
.data("status", status)
.attr("data-status", status)
modifiers = @get("selected_modifiers")
modifiers.push {id: id, status: status}
@set "selected_modifiers", modifiers
if status == "add" and follow_on_modifier_cat?
@showFollowOnModifierCategory follow_on_modifier_cat, modifier
unselectModifier: (modifier) ->
follow_on_modifier_cat = modifier.followOnModifierCategory()
id = modifier.get("id")
$("[data-modifier-id=#{id}]")
.removeData("status")
.removeAttr("data-status")
modifiers = @get("selected_modifiers").filter (m) ->
m.id != id
@set "selected_modifiers", modifiers
if follow_on_modifier_cat?
@hideFollowOnModifierCategory follow_on_modifier_cat
changeModifier: (modifier, status) ->
follow_on_modifier_cat = modifier.followOnModifierCategory()
id = modifier.get("id")
$("[data-modifier-id=#{id}]")
.data("status", status)
.attr("data-status", status)
@get("selected_modifiers").forEach (m) ->
m.status = status if m.id == id
if follow_on_modifier_cat?
if status == "add"
@showFollowOnModifierCategory follow_on_modifier_cat, modifier
else
@hideFollowOnModifierCategory follow_on_modifier_cat
switchStatus: (status) ->
if status == "add"
return "dont_add"
else
return "add"
selectCategory: (category_id) ->
if category_id == "preset"
@set "selected_category", "preset"
html = @htmlOfCategories @get("related_categories")
else
@set "selected_category", DS.ModifierCategory.find(category_id)
html = @htmlOfCategory @get("selected_category")
document
.getElementById("modifiers-list-html")
.innerHTML = html
selectedModifiersInCategory: (category_id, type) ->
if type == "all"
status = "[data-status]"
else
status = "[data-status=#{type}]"
elements = $("[data-category-id=#{category_id}]")
.find(".modifier-button#{status}")
return $.map(elements , (i) ->
$(i).data("modifier-id")
)
# Returns true if the category is over the limit of allowed modifiers
aboveMaxModifiersInCategory: (category) ->
category_id = parseInt category.get("id")
max_in_cat = @get("product").maximumModifierChoices(category_id)
added_modifiers = @selectedModifiersInCategory(category_id, "add").length
return added_modifiers >= max_in_cat
# Returns true if the category is below the limit of required modifiers
belowMinModifiersInCategory: (category) ->
category_id = parseInt category.get("id")
min_in_cat = @get("product").minimumModifierChoices(category_id)
added_modifiers = @selectedModifiersInCategory(category_id, "add").length
return added_modifiers < min_in_cat
# Returns false if any related category does not have the min modifiers selected
modifierRequirementsNotFullfilled: ->
# We need to go to preset categories page since we use the DOM to navigate
# through the categories
@presetButtonPressed()
@get("related_categories").some (c) => @belowMinModifiersInCategory c
# Shows selected modifiers when changing category
showSelectedModifiers: ->
@get("selected_modifiers").forEach (m) =>
$("[data-modifier-id=#{m.id}]")
.data("status", m.status)
.attr("data-status", m.status)
mod = DS.Modifier.find parseInt(m.id)
follow_on_modifier_cat = mod.followOnModifierCategory()
if m.status == "add" and follow_on_modifier_cat?
@showFollowOnModifierCategory follow_on_modifier_cat, mod
showFollowOnModifierCategory: (follow_on_modifier_cat, parent_modifier) ->
$("[data-modifier-id=#{parent_modifier.get('id')}]")
.parents("[data-category-id]")
.find(".items:first")
.append @htmlOfFollowOnModifierCategory(follow_on_modifier_cat, parent_modifier)
hideFollowOnModifierCategory: (follow_on_modifier_cat) ->
category_id = follow_on_modifier_cat.get("id")
follow_on_cat_modifiers = @selectedModifiersInCategory(category_id)
modifiers = @get("selected_modifiers").filter (i) ->
!follow_on_cat_modifiers.some (j) ->
parseInt(i.id) == j
@set "selected_modifiers", modifiers
$("[data-category-id=#{category_id}]").remove()
save: ->
line_item = @get("controller.controllers.order.selectedLineItem")
return if !line_item?
if @modifierRequirementsNotFullfilled()
subtleNiceError "The modifier minimum choices are not fulfilled for all the modifier categories."
return
# Delete current line_item_modifiers and add the new ones
line_item.get("line_item_modifiers").toArray().forEach (lim) =>
lim.delete()
@get("selected_modifiers").forEach (m) ->
modifier = DS.Modifier.find m.id
if m.status == "dont_add"
price = 0
else
price = modifier.get("price")
lim = DS.LineItemModifier.create
line_item: line_item
modifier: modifier
price: price
status: m.status
line_item.get("line_item_modifiers").pushObject lim
@close()
close: ->
@set "controller.active_modifiers", {}
@set "controller.show_modifiers_popup", false
scrollOrderToBottom()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment