removed old view files
This commit is contained in:
parent
71a2ee4673
commit
306e6a85db
Binary file not shown.
Before Width: | Height: | Size: 46 KiB |
Binary file not shown.
Before Width: | Height: | Size: 31 KiB |
Binary file not shown.
Before Width: | Height: | Size: 48 KiB |
@ -10,24 +10,6 @@
|
||||
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
||||
// about supported directives.
|
||||
//
|
||||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
//= require turbolinks
|
||||
//= require bootstrap-sprockets
|
||||
//= require bootstrap-datepicker
|
||||
//= require bootstrap-tagsinput
|
||||
//= require cocoon
|
||||
//= require typeahead
|
||||
//= require autosize
|
||||
//= require chosen.jquery
|
||||
//= require codemirror
|
||||
//= require markdown
|
||||
//= require underscore
|
||||
|
||||
//= require_tree .
|
||||
|
||||
// Setup star rating automagic
|
||||
$(document).on("turbolinks:load", function() {
|
||||
|
||||
$("input[data-rating='true']").starRating();
|
||||
|
||||
});
|
@ -1,124 +0,0 @@
|
||||
(function($) {
|
||||
|
||||
function State() {
|
||||
this.input = null;
|
||||
this.outputUnit = null;
|
||||
this.density = null;
|
||||
this.changed = false;
|
||||
}
|
||||
|
||||
State.prototype.setInput = function(value) {
|
||||
if (value != this.input) {
|
||||
this.changed = true;
|
||||
this.input = value;
|
||||
}
|
||||
};
|
||||
|
||||
State.prototype.setDensity = function(value) {
|
||||
if (value != this.density) {
|
||||
this.changed = true;
|
||||
this.density = value;
|
||||
}
|
||||
};
|
||||
|
||||
State.prototype.setOutputUnit = function(value) {
|
||||
if (value != this.outputUnit) {
|
||||
this.changed = true;
|
||||
this.outputUnit = value;
|
||||
}
|
||||
};
|
||||
|
||||
State.prototype.is_changed = function() {
|
||||
return this.changed;
|
||||
};
|
||||
|
||||
State.prototype.reset = function() {
|
||||
this.changed = false;
|
||||
};
|
||||
|
||||
var ingredientSearchEngine = new Bloodhound({
|
||||
initialize: false,
|
||||
datumTokenizer: function(datum) {
|
||||
return Bloodhound.tokenizers.whitespace(datum.name);
|
||||
},
|
||||
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||||
identify: function(datum) { return datum.id; },
|
||||
sorter: function(a, b) {
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
} else if (b.name < a.name) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
remote: {
|
||||
url: '/calculator/ingredient_search.json?query=%QUERY',
|
||||
wildcard: '%QUERY'
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("turbolinks:load", function() {
|
||||
|
||||
var state = new State();
|
||||
var $input = $("#input");
|
||||
var $output = $("#output");
|
||||
var $density = $("#density");
|
||||
var $outputUnit = $("#output_unit");
|
||||
|
||||
var performUpdate = _.debounce(function() {
|
||||
$.getJSON(
|
||||
"/calculator/calculate",
|
||||
{input: $input.val(), output_unit: $outputUnit.val(), density: $density.val()},
|
||||
function(data) {
|
||||
if (data.errors.length) {
|
||||
$("#errors_panel").show();
|
||||
$("#errors_container").html(data.errors.join(" "));
|
||||
} else {
|
||||
$("#errors_panel").hide();
|
||||
}
|
||||
|
||||
$output.val(data.output);
|
||||
}
|
||||
);
|
||||
|
||||
}, 500);
|
||||
|
||||
var ingredientPicked = function(i) {
|
||||
$density.val(i.density);
|
||||
$density.trigger('change');
|
||||
};
|
||||
|
||||
$input.add($outputUnit).add($density).on('change blur keyup', function(evt) {
|
||||
state.setInput($input.val());
|
||||
state.setOutputUnit($outputUnit.val());
|
||||
state.setDensity($density.val());
|
||||
|
||||
if (state.is_changed()) {
|
||||
performUpdate();
|
||||
state.reset();
|
||||
}
|
||||
});
|
||||
|
||||
if ($("#calculator").length) {
|
||||
ingredientSearchEngine.initialize(false);
|
||||
|
||||
$("#ingredient").typeahead({},
|
||||
{
|
||||
name: 'ingredients',
|
||||
source: ingredientSearchEngine,
|
||||
display: function(datum) {
|
||||
return datum.name;
|
||||
}
|
||||
})
|
||||
.on("typeahead:select", function(evt, value) {
|
||||
ingredientPicked(value);
|
||||
})
|
||||
.on("typeahead:autocomplete", function(evt, value) {
|
||||
ingredientPicked(value);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})(jQuery);
|
@ -1,36 +0,0 @@
|
||||
(function($) {
|
||||
|
||||
var pluginName = "checkable";
|
||||
|
||||
var defaultOptions = {
|
||||
childrenSelector: "li",
|
||||
selectedClass: "checked"
|
||||
};
|
||||
|
||||
var methods = {
|
||||
initialize: function (opts, sources) {
|
||||
|
||||
return this.each(function() {
|
||||
var options = $.extend({}, defaultOptions, opts);
|
||||
$(this).on("click", options.childrenSelector, function(evt) {
|
||||
$(evt.currentTarget).toggleClass(options.selectedClass);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var privateMethods = {
|
||||
|
||||
};
|
||||
|
||||
$.fn[pluginName] = function (method) {
|
||||
if (methods[method]) {
|
||||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
} else if (typeof method === 'object' || ! method) {
|
||||
return methods.initialize.apply(this, arguments);
|
||||
} else {
|
||||
$.error('Method ' + method + ' does not exist on jQuery.' + pluginName);
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
@ -1,46 +0,0 @@
|
||||
|
||||
var flashMessageTypeMap = {
|
||||
error: "danger",
|
||||
notice: "success"
|
||||
};
|
||||
|
||||
function flashMessage(flashType, message) {
|
||||
|
||||
var timeoutIdContainer = {};
|
||||
|
||||
if (flashMessageTypeMap[flashType]) {
|
||||
flashType = flashMessageTypeMap[flashType];
|
||||
}
|
||||
|
||||
var closeButton = $("<button type='button' />")
|
||||
.addClass("close")
|
||||
.append($("<span />").html("×"))
|
||||
.bind("click.Flash", function() { $(this).parent().hide({effect: "fade", duration: 1000}); clearTimeout(timeoutIdContainer.id); });
|
||||
|
||||
var $flashDiv = $("<div></div>")
|
||||
.html(message)
|
||||
.append(closeButton)
|
||||
.addClass("popup")
|
||||
.addClass("alert")
|
||||
.addClass("alert-" + flashType)
|
||||
.hide()
|
||||
.appendTo("#flashContainer")
|
||||
.show({effect: "pulsate", times: 1, duration: 1500});
|
||||
|
||||
timeoutIdContainer.id = setTimeout(function() {
|
||||
$flashDiv.unbind(".Flash");
|
||||
$flashDiv.hide({effect: "fade", duration: 1000});
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
$(document).on("turbolinks:load", function() {
|
||||
$("#flashHolder").find("div").each(function(idx, div) {
|
||||
var $div = $(div);
|
||||
var type = $div.attr("class");
|
||||
var message = $div.html();
|
||||
|
||||
$div.remove();
|
||||
|
||||
flashMessage(type, message);
|
||||
});
|
||||
});
|
@ -1,88 +0,0 @@
|
||||
|
||||
window.INGREDIENT_API = {};
|
||||
|
||||
(function($) {
|
||||
|
||||
function initializeEditor($ingredientForm) {
|
||||
usdaFoodSearchEngine.initialize(false);
|
||||
|
||||
var $typeahead = $ingredientForm.find(".ndbn_typeahead");
|
||||
|
||||
$typeahead.typeahead_search({
|
||||
searchUrl: '/ingredients/usda_food_search.html',
|
||||
resultsContainer: $ingredientForm.find(".ndbn_results")
|
||||
},{
|
||||
name: 'usdaFoods',
|
||||
source: usdaFoodSearchEngine,
|
||||
limit: 20,
|
||||
display: function(datum) {
|
||||
return datum.name;
|
||||
}
|
||||
});
|
||||
|
||||
$typeahead.on("typeahead_search:selected", function(evt, item) {
|
||||
selectNdbn($ingredientForm, item.ndbn);
|
||||
});
|
||||
|
||||
$ingredientForm.on("click", ".ndbn_results .food_result", function(evt) {
|
||||
var $item = $(evt.target);
|
||||
var ndbn = $item.data("ndbn");
|
||||
selectNdbn($ingredientForm, ndbn);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function selectNdbn($ingredientForm, ndbn) {
|
||||
var id = $ingredientForm.find("input.id").val();
|
||||
|
||||
$ingredientForm.find("input.ndbn").val(ndbn);
|
||||
$ingredientForm.attr("action", "/ingredients/" + id + "/select_ndbn").attr("data-remote", "true");
|
||||
|
||||
$ingredientForm.submit();
|
||||
}
|
||||
|
||||
function customTokenizer(str) {
|
||||
if (str) {
|
||||
var cleaned = str.replace(/,/g, "");
|
||||
return Bloodhound.tokenizers.whitespace(cleaned);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
window.INGREDIENT_API.initialize = function() {
|
||||
var $ingredientForm = $("#ingredient_form");
|
||||
|
||||
if ($ingredientForm.length) {
|
||||
initializeEditor($ingredientForm);
|
||||
}
|
||||
};
|
||||
|
||||
var usdaFoodSearchEngine = new Bloodhound({
|
||||
initialize: false,
|
||||
datumTokenizer: function(datum) {
|
||||
var str = datum ? datum.name : null;
|
||||
return customTokenizer(str);
|
||||
},
|
||||
queryTokenizer: customTokenizer,
|
||||
identify: function(datum) { return datum.ndbn; },
|
||||
sorter: function(a, b) {
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
} else if (b.name < a.name) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
remote: {
|
||||
url: '/ingredients/usda_food_search.json?query=%QUERY',
|
||||
wildcard: '%QUERY'
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("turbolinks:load", function() {
|
||||
window.INGREDIENT_API.initialize();
|
||||
});
|
||||
|
||||
})(jQuery);
|
@ -1,7 +0,0 @@
|
||||
(function($) {
|
||||
|
||||
$(document).on("turbolinks:load", function() {
|
||||
$(".log-form input.datepicker").datepicker({autoclose: true, todayBtn: "linked", format: "yyyy-mm-dd"});
|
||||
});
|
||||
|
||||
})(jQuery);
|
@ -1,393 +0,0 @@
|
||||
(function($) {
|
||||
|
||||
var ingredientSearchEngine = new Bloodhound({
|
||||
initialize: false,
|
||||
datumTokenizer: function(datum) {
|
||||
return Bloodhound.tokenizers.whitespace(datum.name);
|
||||
},
|
||||
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||||
identify: function(datum) { return datum.id; },
|
||||
sorter: function(a, b) {
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
} else if (b.name < a.name) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
prefetch: {
|
||||
url: '/ingredients/prefetch.json',
|
||||
cache: false
|
||||
},
|
||||
remote: {
|
||||
url: '/ingredients/search.json?query=%QUERY',
|
||||
wildcard: '%QUERY'
|
||||
}
|
||||
});
|
||||
|
||||
// var tagSearchEngine = new Bloodhound({
|
||||
// initialize: false,
|
||||
// datumTokenizer: function(datum) {
|
||||
// return Bloodhound.tokenizers.whitespace(datum.name);
|
||||
// },
|
||||
// queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||||
// identify: function(datum) { return datum.id; },
|
||||
// sorter: function(a, b) {
|
||||
// if (a.name < b.name) {
|
||||
// return -1;
|
||||
// } else if (b.name < a.name) {
|
||||
// return 1;
|
||||
// } else {
|
||||
// return 0;
|
||||
// }
|
||||
// },
|
||||
// prefetch: {
|
||||
// url: '/tags/prefetch.json',
|
||||
// cache: false
|
||||
// },
|
||||
// remote: {
|
||||
// url: '/tags/search.json?query=%QUERY',
|
||||
// wildcard: '%QUERY'
|
||||
// }
|
||||
// });
|
||||
|
||||
function reorder($container) {
|
||||
$container.find("div.nested-fields").each(function(idx, editor) {
|
||||
var $editor = $(editor);
|
||||
$editor.find('input.sort_order').val(idx + 1).trigger("changed");
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function initializeIngredientEditor($container, ingredientSearchEngine) {
|
||||
// $container is either an element that contains many editors, or a single editor.
|
||||
var $editors = $container.find(".ingredient-typeahead").closest(".nested-fields");
|
||||
|
||||
$editors.each(function(idx, elem) {
|
||||
var $editor = $(elem);
|
||||
var $ingredientId = $editor.find("input.ingredient_id");
|
||||
var $group = $editor.find("div.typeahead-group");
|
||||
|
||||
$editor.find(".ingredient-typeahead").typeahead({},
|
||||
{
|
||||
name: 'ingredients',
|
||||
source: ingredientSearchEngine,
|
||||
display: function(datum) {
|
||||
return datum.name;
|
||||
}
|
||||
});
|
||||
|
||||
if ($ingredientId.val().length) {
|
||||
$group.addClass("has-success");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function ingredientItemPicked($typeahead, datum) {
|
||||
var $container = $typeahead.closest(".nested-fields");
|
||||
var $ingredientId = $container.find("input.ingredient_id");
|
||||
var $group = $container.find("div.typeahead-group");
|
||||
|
||||
$ingredientId.val(datum.id);
|
||||
$typeahead.typeahead('val', datum.name);
|
||||
$group.addClass("has-success");
|
||||
}
|
||||
|
||||
function ingredientNameChange($typeahead, ingredientSearchEngine) {
|
||||
var $container = $typeahead.closest(".nested-fields");
|
||||
var $ingredientId = $container.find("input.ingredient_id");
|
||||
var $group = $container.find("div.typeahead-group");
|
||||
|
||||
var id = $ingredientId.val();
|
||||
var value = $typeahead.typeahead('val');
|
||||
|
||||
if (id && id.length) {
|
||||
var found = ingredientSearchEngine.get([id]);
|
||||
if (found && found[0] && found[0].name != value) {
|
||||
// User has chosen something custom
|
||||
$ingredientId.val('');
|
||||
|
||||
$group.removeClass("has-success");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addIngredient(item) {
|
||||
$("#ingredient-list").one("cocoon:before-insert", function(e, $container) {
|
||||
var $ingredientId = $container.find("input.ingredient_id");
|
||||
var $name = $container.find("input.ingredient-typeahead");
|
||||
var $quantity = $container.find("input.quantity");
|
||||
var $units = $container.find("input.units");
|
||||
var $preparation = $container.find("input.preparation");
|
||||
|
||||
$name.val(item.name);
|
||||
$ingredientId.val(item.ingredient_id);
|
||||
$units.val(item.units);
|
||||
$quantity.val(item.quantity);
|
||||
$preparation.val(item.preparation);
|
||||
});
|
||||
|
||||
$("#addIngredientButton").trigger("click");
|
||||
}
|
||||
|
||||
function getIngredients() {
|
||||
var data = [];
|
||||
$("#ingredient-list .ingredient-editor").each(function() {
|
||||
var $container = $(this);
|
||||
|
||||
var $ingredientId = $container.find("input.ingredient_id");
|
||||
var $name = $container.find("input.ingredient-typeahead.tt-input");
|
||||
var $quantity = $container.find("input.quantity");
|
||||
var $units = $container.find("input.units");
|
||||
var $preparation = $container.find("input.preparation");
|
||||
|
||||
data.push({ingredient_id: $ingredientId.val(), name: $name.typeahead("val"), quantity: $quantity.val(), units: $units.val(), preparation: $preparation.val()});
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
$(document).on("turbolinks:load", function() {
|
||||
|
||||
var $ingredientList = $("#ingredient-list");
|
||||
var $tagInput = $("select.tag_names");
|
||||
var $stepInput = $("textarea#recipe_step_text");
|
||||
|
||||
if ($ingredientList.length) {
|
||||
ingredientSearchEngine.initialize(false);
|
||||
}
|
||||
|
||||
if ($stepInput.length) {
|
||||
CodeMirror.fromTextArea(
|
||||
$stepInput[0],
|
||||
{
|
||||
mode: {
|
||||
name: 'markdown',
|
||||
strikethrough: true
|
||||
},
|
||||
// config tomfoolery to enable soft tabs
|
||||
extraKeys: {
|
||||
Tab: function(cm) {
|
||||
if (cm.somethingSelected()) {
|
||||
cm.indentSelection("add");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cm.options.indentWithTabs)
|
||||
cm.replaceSelection("\t", "end", "+input");
|
||||
else
|
||||
cm.execCommand("insertSoftTab");
|
||||
},
|
||||
"Shift-Tab": function(cm) {
|
||||
cm.indentSelection("subtract");
|
||||
}
|
||||
},
|
||||
indentUnit: 2,
|
||||
tabSize: 2,
|
||||
indentWithTabs: false,
|
||||
lineWrapping: true,
|
||||
lineNumbers: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$tagInput.tagsinput({
|
||||
trimValue: true,
|
||||
confirmKeys: [9, 13, 32, 44] // tab, enter, space, comma
|
||||
});
|
||||
|
||||
|
||||
initializeIngredientEditor($ingredientList, ingredientSearchEngine);
|
||||
|
||||
$ingredientList
|
||||
.on("cocoon:after-insert", function(e, item) {
|
||||
reorder($ingredientList);
|
||||
initializeIngredientEditor(item, ingredientSearchEngine);
|
||||
})
|
||||
.on("cocoon:after-remove", function(e, item) {
|
||||
if (item.find(".remove-button.existing").length) {
|
||||
item.detach().appendTo("#deleted_ingredients");
|
||||
}
|
||||
reorder($ingredientList);
|
||||
})
|
||||
.on("typeahead:change", function(evt, value) {
|
||||
ingredientNameChange($(evt.target), ingredientSearchEngine);
|
||||
})
|
||||
.on("typeahead:select", function(evt, value) {
|
||||
ingredientItemPicked($(evt.target), value);
|
||||
})
|
||||
.on("typeahead:autocomplete", function(evt, value) {
|
||||
ingredientItemPicked($(evt.target), value);
|
||||
})
|
||||
.on("click", "button.ingredient_convert_btn", function(evt) {
|
||||
|
||||
});
|
||||
|
||||
$('#convert_modal')
|
||||
.on('show.bs.modal', function (event) {
|
||||
var $button = $(event.relatedTarget);
|
||||
var $modal = $(this);
|
||||
|
||||
var $editor = $button.closest(".ingredient-editor");
|
||||
|
||||
$modal.data('ingredient-editor', $editor);
|
||||
|
||||
var $quantity = $editor.find("input.quantity");
|
||||
var $units = $editor.find("input.units");
|
||||
var $ingredientId = $editor.find("input.ingredient_id");
|
||||
|
||||
var $modalQuantity = $modal.find("input.quantity");
|
||||
var $modalUnits = $modal.find("input.units");
|
||||
var $modalIngredientId = $modal.find("input.ingredient_id");
|
||||
|
||||
$modalQuantity.val($quantity.val());
|
||||
$modalUnits.val($units.val());
|
||||
$modalIngredientId.val($ingredientId.val());
|
||||
})
|
||||
.on("ajax:success", "form", function(evt, data, status, xhr) {
|
||||
var $modal = $("#convert_modal");
|
||||
var $editor = $modal.data('ingredient-editor');
|
||||
|
||||
if (data.success) {
|
||||
var $quantity = $editor.find("input.quantity");
|
||||
var $units = $editor.find("input.units");
|
||||
|
||||
var $modalOutUnits = $modal.find("input.output_units");
|
||||
|
||||
$quantity.val(data.output_quantity);
|
||||
if ($modalOutUnits.val().length) {
|
||||
$units.val($modalOutUnits.val());
|
||||
}
|
||||
|
||||
$modal.modal('hide');
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
$("#modal_form_container").replaceWith($(data.form_html));
|
||||
});
|
||||
|
||||
var $bulkIngredientsModal = $("#bulk_ingredients_modal");
|
||||
var $ingredientBulkInput = $("#ingredient_bulk_input");
|
||||
var $ingredientBulkList = $("#ingredient_bulk_parsed_list");
|
||||
autosize($ingredientBulkInput);
|
||||
|
||||
var parseBulkIngredients = function() {
|
||||
var data = $ingredientBulkInput.val();
|
||||
$ingredientBulkList.empty();
|
||||
|
||||
var parsed = [];
|
||||
var x;
|
||||
|
||||
var lines = data.replace("\r", "").split("\n");
|
||||
|
||||
var regex = /^(?:([\d\/.]+(?:\s+[\d\/]+)?)\s+)?(?:([\w-]+)(?:\s+of)?\s+)?([^,]*)(?:,\s*(.*))?$/i;
|
||||
|
||||
var magicFunc = function(str) {
|
||||
if (str == "-") {
|
||||
return "";
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
for (x = 0; x < lines.length; x++) {
|
||||
var line = lines[x].trim();
|
||||
if (line.length == 0) { continue; }
|
||||
|
||||
var barIndex = line.lastIndexOf("|");
|
||||
var afterBar = null;
|
||||
|
||||
if (barIndex >= 0) {
|
||||
afterBar = line.slice(barIndex + 1);
|
||||
line = line.slice(0, barIndex);
|
||||
}
|
||||
|
||||
var match = line.match(regex);
|
||||
|
||||
if (match) {
|
||||
item = {quantity: magicFunc(match[1]), units: magicFunc(match[2]), name: magicFunc(match[3]), preparation: magicFunc(match[4])};
|
||||
if (afterBar) {
|
||||
item.name = item.name + ", " + item.preparation;
|
||||
item.preparation = afterBar;
|
||||
}
|
||||
parsed.push(item);
|
||||
} else {
|
||||
parsed.push(null);
|
||||
}
|
||||
}
|
||||
|
||||
$bulkIngredientsModal.data("bulkData", parsed);
|
||||
|
||||
for (x = 0; x < parsed.length; x++) {
|
||||
var item = parsed[x];
|
||||
if (item != null) {
|
||||
$ingredientBulkList.append(
|
||||
$("<tr />")
|
||||
.append($("<td />").addClass("quantity").text(item.quantity))
|
||||
.append($("<td />").addClass("units").text(item.units))
|
||||
.append($("<td />").addClass("name").text(item.name))
|
||||
.append($("<td />").addClass("preparation").text(item.preparation))
|
||||
);
|
||||
} else {
|
||||
$ingredientBulkList.append(
|
||||
$("<tr />")
|
||||
.append($("<td />").attr("colspan", "4").text("<Cannot Parse>"))
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$bulkIngredientsModal
|
||||
.on('show.bs.modal', function (event) {
|
||||
var data = getIngredients();
|
||||
var x;
|
||||
var text = [];
|
||||
|
||||
for (x = 0; x < data.length; x++) {
|
||||
var item = data[x];
|
||||
|
||||
text.push(
|
||||
item.quantity + " " +
|
||||
(item.units || "-") + " " +
|
||||
item.name +
|
||||
(item.preparation ? (", " + item.preparation) : "")
|
||||
);
|
||||
}
|
||||
|
||||
$ingredientBulkInput.val(text.join("\n"));
|
||||
|
||||
setTimeout(function() {
|
||||
parseBulkIngredients();
|
||||
autosize.update($ingredientBulkInput);
|
||||
}, 250);
|
||||
});
|
||||
|
||||
$ingredientBulkInput.on('keyup', function() {
|
||||
parseBulkIngredients();
|
||||
});
|
||||
|
||||
$("#bulkIngredientAddSubmit").on("click", function() {
|
||||
var parsed = $bulkIngredientsModal.data("bulkData");
|
||||
var x;
|
||||
|
||||
$("#ingredient-list").find(".remove-button").trigger("click");
|
||||
|
||||
if (parsed && parsed.length) {
|
||||
for (x = 0; x < parsed.length; x++) {
|
||||
var item = parsed[x];
|
||||
if (item) {
|
||||
addIngredient(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$bulkIngredientsModal.modal('hide')
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
})(jQuery);
|
@ -1,33 +0,0 @@
|
||||
(function($) {
|
||||
|
||||
$(document).on("turbolinks:load", function() {
|
||||
$(".recipe-view ul.ingredients").checkable();
|
||||
$(".recipe-view div.steps ol").checkable();
|
||||
|
||||
var $searchBtn = $("#recipe_index_search_button");
|
||||
|
||||
if ($searchBtn.length) {
|
||||
var $form = $("#search_form");
|
||||
var $nameInput = $("#name_search");
|
||||
var $tagsInput = $("#tags_search");
|
||||
|
||||
$form.submit(function() {
|
||||
$("#criteria_name").val($nameInput.val());
|
||||
$("#criteria_tags").val($tagsInput.val());
|
||||
});
|
||||
|
||||
$searchBtn.click(function(evt) {
|
||||
$form.submit();
|
||||
});
|
||||
|
||||
$nameInput.add($tagsInput).on('keydown', function(evt) {
|
||||
if (evt.which == 13) {
|
||||
console.log('keydown enter pressed');
|
||||
$form.submit();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery);
|
@ -1,133 +0,0 @@
|
||||
|
||||
(function ($) {
|
||||
|
||||
var pluginName = "starRating";
|
||||
var defaultOptions = {
|
||||
starCount: 5,
|
||||
readOnly: false,
|
||||
interval: 1,
|
||||
size: '30px'
|
||||
};
|
||||
|
||||
var methods = {
|
||||
initialize: function(opts) {
|
||||
return this.each(function() {
|
||||
var $input = $(this);
|
||||
var attrOpts = {};
|
||||
var inputData = $input.data();
|
||||
|
||||
if (inputData[pluginName.toLowerCase()] === true) {
|
||||
if (console && console.log) {
|
||||
console.log("star rating has already been initialized; skipping...");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$input.attr("data-" + pluginName.toLowerCase(), "true");
|
||||
|
||||
if (inputData.interval) {
|
||||
attrOpts.interval = inputData.interval;
|
||||
}
|
||||
|
||||
if (inputData.starcount) {
|
||||
attrOpts.starCount = inputData.starcount;
|
||||
}
|
||||
|
||||
if (inputData.size) {
|
||||
attrOpts.size = inputData.size;
|
||||
}
|
||||
|
||||
if ($input.is(":disabled")) {
|
||||
attrOpts.readOnly = true;
|
||||
}
|
||||
|
||||
var options = _.extend({}, defaultOptions, attrOpts, opts);
|
||||
|
||||
var $widget = $("<span />").addClass("star-rating").css({'font-size': options.size});
|
||||
var $emptySet = $("<span />").addClass("empty-set").appendTo($widget);
|
||||
var $filledSet = $("<span />").addClass("filled-set").appendTo($widget);
|
||||
|
||||
options.$input = $input;
|
||||
options.$emptySet = $emptySet;
|
||||
|
||||
for (var x = 1; x <= options.starCount; x++) {
|
||||
$emptySet.append(
|
||||
$("<span />").addClass("star empty")
|
||||
);
|
||||
|
||||
$filledSet.append(
|
||||
$("<span />").addClass("star full")
|
||||
);
|
||||
}
|
||||
|
||||
$widget.data(pluginName + ".options", options);
|
||||
|
||||
$input.data(pluginName, true).hide().after($widget);
|
||||
|
||||
privateMethods.updateStars($widget);
|
||||
|
||||
if (!options.readOnly) {
|
||||
$widget
|
||||
.on("click." + pluginName, function(e) {
|
||||
var value = privateMethods.calculateRating($widget, e.pageX);
|
||||
privateMethods.setValue($widget, value);
|
||||
})
|
||||
.on("mousemove." + pluginName, function(e) {
|
||||
var value = privateMethods.calculateRating($widget, e.pageX);
|
||||
privateMethods.updateStars($widget, value);
|
||||
})
|
||||
.on("mouseleave." + pluginName, function (e) {
|
||||
privateMethods.updateStars($widget);
|
||||
});
|
||||
}
|
||||
|
||||
$input
|
||||
.on("change." + pluginName, function() {
|
||||
privateMethods.updateStars($widget);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var privateMethods = {
|
||||
updateStars: function($widget, value) {
|
||||
var options = $widget.data(pluginName + ".options");
|
||||
value = (value == null ? (parseFloat(options.$input.val() || 0)) : value);
|
||||
$widget.find(".filled-set").css({width: privateMethods.calculateWidth($widget, value)});
|
||||
},
|
||||
|
||||
setValue: function($widget, value) {
|
||||
var options = $widget.data(pluginName + ".options");
|
||||
options.$input.val(value);
|
||||
privateMethods.updateStars($widget);
|
||||
},
|
||||
|
||||
calculateWidth: function($widget, value) {
|
||||
var options = $widget.data(pluginName + ".options");
|
||||
var width = options.$emptySet.width();
|
||||
return ((value / options.starCount) * 100).toString() + "%";
|
||||
},
|
||||
|
||||
// Given a screen X coordinate, calculates the nearest valid value for this rating widget
|
||||
calculateRating: function($widget, screenX) {
|
||||
var options = $widget.data(pluginName + ".options");
|
||||
var offset = options.$emptySet.offset();
|
||||
var width = options.$emptySet.width();
|
||||
var ratio = (screenX - offset.left) / width;
|
||||
ratio = Math.max(0, Math.min(1, ratio));
|
||||
|
||||
return Math.ceil(options.starCount * (1 / options.interval) * ratio) / (1 / options.interval);
|
||||
}
|
||||
};
|
||||
|
||||
$.fn[pluginName] = function(method) {
|
||||
if (methods[method]) {
|
||||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
} else if (typeof method === 'object' || ! method) {
|
||||
return methods.initialize.apply(this, arguments);
|
||||
} else {
|
||||
$.error('Method ' + method + ' does not exist on jQuery.' + pluginName);
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
@ -1,112 +0,0 @@
|
||||
(function($) {
|
||||
|
||||
var pluginName = "typeahead_search";
|
||||
|
||||
var defaultOptions = {
|
||||
};
|
||||
|
||||
var methods = {
|
||||
initialize: function (opts, sources) {
|
||||
|
||||
return this.each(function() {
|
||||
var options = $.extend({}, defaultOptions, opts);
|
||||
var $this = $(this);
|
||||
|
||||
if ($this.data(pluginName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(pluginName, {options: options});
|
||||
|
||||
var $inputGroup = $('<div class="input-group" />');
|
||||
var $btnSpan = $("<span class='input-group-btn' />");
|
||||
var $btn = $("<button class='btn btn-default' type='button' />").append($("<span />").addClass("glyphicon glyphicon-search"));
|
||||
|
||||
$btnSpan.append($btn);
|
||||
|
||||
$this.after($inputGroup);
|
||||
$this.detach();
|
||||
$inputGroup.append($this);
|
||||
$inputGroup.append($btnSpan);
|
||||
|
||||
$this.typeahead(opts, sources);
|
||||
|
||||
$btn.on("click", function(evt) {
|
||||
privateMethods.search($this);
|
||||
});
|
||||
|
||||
$this
|
||||
.on("typeahead:change", function(evt, value) {
|
||||
privateMethods.change($this, value);
|
||||
})
|
||||
.on("typeahead:select", function(evt, value) {
|
||||
privateMethods.select($this, value);
|
||||
})
|
||||
.on("typeahead:autocomplete", function(evt, value) {
|
||||
privateMethods.autocomplete($this, value);
|
||||
})
|
||||
.on("keydown", function(evt) {
|
||||
if (evt.keyCode == 13) {
|
||||
evt.preventDefault();
|
||||
privateMethods.search($this);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
val: function() {
|
||||
if (this.length) {
|
||||
return $(this[0]).typeahead("val");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var privateMethods = {
|
||||
change: function($this, value) {
|
||||
|
||||
},
|
||||
|
||||
select: function($this, item) {
|
||||
$this.trigger("typeahead_search:selected", item);
|
||||
},
|
||||
|
||||
autocomplete: function($this, item) {
|
||||
$this.trigger("typeahead_search:selected", item);
|
||||
},
|
||||
|
||||
search: function($this) {
|
||||
var options = privateMethods.options($this);
|
||||
var input = $this.typeahead("val");
|
||||
|
||||
if (input.length && options.searchUrl && options.searchUrl.length) {
|
||||
$.get({
|
||||
url: options.searchUrl,
|
||||
data: {query: input},
|
||||
dataType: 'html',
|
||||
success: function(data) {
|
||||
$(options.resultsContainer).empty().append(data);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$this.typeahead("close");
|
||||
},
|
||||
|
||||
options: function($this) {
|
||||
return $this.data(pluginName).options;
|
||||
}
|
||||
};
|
||||
|
||||
$.fn[pluginName] = function (method) {
|
||||
if (methods[method]) {
|
||||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
} else if (typeof method === 'object' || ! method) {
|
||||
return methods.initialize.apply(this, arguments);
|
||||
} else {
|
||||
$.error('Method ' + method + ' does not exist on jQuery.' + pluginName);
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
@ -1,70 +0,0 @@
|
||||
(function($) {
|
||||
|
||||
var pluginName = "typeahead_selector";
|
||||
|
||||
var defaultOptions = {
|
||||
};
|
||||
|
||||
var methods = {
|
||||
initialize: function (opts, sources) {
|
||||
|
||||
return this.each(function() {
|
||||
var options = $.extend({}, defaultOptions, opts);
|
||||
var $this = $(this);
|
||||
$this.typeahead(opts, sources);
|
||||
|
||||
$this
|
||||
.on("typeahead:change", function(evt, value) {
|
||||
privateMethods.change($this, value);
|
||||
})
|
||||
.on("typeahead:select", function(evt, value) {
|
||||
privateMethods.select($this, value);
|
||||
})
|
||||
.on("typeahead:autocomplete", function(evt, value) {
|
||||
privateMethods.autocomplete($this, value);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
val: function() {
|
||||
if (this.length) {
|
||||
return $(this[0]).data('typeahead_selected');
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var privateMethods = {
|
||||
change: function($this, value) {
|
||||
var item = $this.data('typeahead_pending');
|
||||
if (item) {
|
||||
$this.data('typeahead_pending', null);
|
||||
$this.data('typeahead_selected', item);
|
||||
$this.trigger("typeahead_selector:selected", item);
|
||||
} else {
|
||||
$this.data('typeahead_selected', null);
|
||||
$this.trigger("typeahead_selector:invalid", value);
|
||||
}
|
||||
},
|
||||
|
||||
select: function($this, item) {
|
||||
$this.data('typeahead_pending', item);
|
||||
},
|
||||
|
||||
autocomplete: function($this, item) {
|
||||
$this.data('typeahead_pending', item);
|
||||
}
|
||||
};
|
||||
|
||||
$.fn[pluginName] = function (method) {
|
||||
if (methods[method]) {
|
||||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
} else if (typeof method === 'object' || ! method) {
|
||||
return methods.initialize.apply(this, arguments);
|
||||
} else {
|
||||
$.error('Method ' + method + ' does not exist on jQuery.' + pluginName);
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
@ -10,97 +10,4 @@
|
||||
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
||||
* file per style scope.
|
||||
*
|
||||
*= require flash_messages
|
||||
*= require chosen
|
||||
*= require codemirror
|
||||
*= require bootstrap-datepicker3
|
||||
*= require bootstrap-tagsinput
|
||||
*= require font_references
|
||||
*/
|
||||
|
||||
@import "bootstrap-sprockets";
|
||||
@import "journal_custom_colors";
|
||||
@import "journal/_variables";
|
||||
@import "bootstrap";
|
||||
@import "journal/_bootswatch";
|
||||
|
||||
@import "typeahead-bootstrap";
|
||||
@import "recipes";
|
||||
@import "star_rating";
|
||||
@import "codemirror_custom";
|
||||
|
||||
// Skin overrides
|
||||
.has-error {
|
||||
.help-block,
|
||||
.control-label,
|
||||
.radio,
|
||||
.checkbox,
|
||||
.radio-inline,
|
||||
.checkbox-inline,
|
||||
&.radio label,
|
||||
&.checkbox label,
|
||||
&.radio-inline label,
|
||||
&.checkbox-inline label,
|
||||
.form-control-feedback {
|
||||
color: $state-danger-text;
|
||||
}
|
||||
|
||||
.form-control,
|
||||
.form-control:focus {
|
||||
border-color: $state-danger-border;
|
||||
}
|
||||
}
|
||||
|
||||
$footer_height: 40px;
|
||||
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
body {
|
||||
/* Margin bottom by footer height */
|
||||
margin-bottom: $footer_height + 20;
|
||||
background: image_url("grey_wash_wall.png");
|
||||
}
|
||||
|
||||
#main_container {
|
||||
background: white;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: $footer_height;
|
||||
background-color: $gray-lighter;
|
||||
border-top: solid 1px $gray-light;
|
||||
}
|
||||
|
||||
a.sorted {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
a.sorted.asc:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
margin: 8px 0 0 6px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
|
||||
border-top: 5px solid black;
|
||||
}
|
||||
|
||||
a.sorted.desc:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
margin: 8px 0 0 6px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
|
||||
border-bottom: 5px solid black;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
.CodeMirror {
|
||||
border: 1px solid $gray-light;
|
||||
font-family: inconsolata monospace;
|
||||
font-size: 15px;
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
|
||||
#flashHolder {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#flashContainer {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.alert.popup {
|
||||
position: fixed;
|
||||
top: 15px;
|
||||
left: 50%;
|
||||
width: 30em;
|
||||
margin-left: -15em;
|
||||
}
|
||||
|
||||
.alert {
|
||||
}
|
||||
|
||||
.flash {
|
||||
text-align: center;
|
||||
width: 30em;
|
||||
padding: 5px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: #fff6ff;
|
||||
border: 3px solid #fda8a8;
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: #ffffdd;
|
||||
border: 3px solid #ffdd00;
|
||||
}
|
||||
|
||||
.notice {
|
||||
background-color: #D8F6CE;
|
||||
border: 3px solid #3ADF00;
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), font_url("open-sans-light.woff2") format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans'), local('OpenSans'), font_url("open-sans.woff2") format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), font_url("open-sans-bold.woff2") format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), font_url("open-sans-italic.woff2") format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSansLight-Italic'), font_url("open-sans-light-italic.woff2") format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), font_url("open-sans-bold-italic.woff2") format('woff2');
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Roboto Light'), local('Roboto-Light'), font_url("roboto-light.woff2") format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), font_url("roboto.woff2") format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), font_url("roboto-medium.woff2") format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), font_url("roboto-bold.woff2") format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Raleway';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Raleway'), font_url("raleway.woff2") format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Raleway';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Raleway Bold'), local('Raleway-Bold'), font_url("raleway-bold.woff2") format('woff2');
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'News Cycle';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('News Cycle'), local('NewsCycle'), font_url("news-cycle.woff2") format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'News Cycle';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('News Cycle Bold'), local('NewsCycle-Bold'), font_url("news-cycle-bold.woff2") format('woff2');
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inconsolata';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Inconsolata Regular'), local('Inconsolata-Regular'), font_url('inconsolata.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inconsolata';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Inconsolata Bold'), local('Inconsolata-Bold'), font_url('inconsolata-bold.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the ingredients controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
@ -1,5 +0,0 @@
|
||||
|
||||
$brand-primary: darken(#93C54B, 10%);
|
||||
$brand-success: $brand-primary;
|
||||
$text-color: #3E3F3A;
|
||||
|
@ -1,76 +0,0 @@
|
||||
// Place all the styles related to the recipes controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
|
||||
div.table_tags {
|
||||
width: 275px;
|
||||
}
|
||||
|
||||
div.recipe_list_controls {
|
||||
width: 85px;
|
||||
}
|
||||
|
||||
@mixin editor {
|
||||
|
||||
@extend .well;
|
||||
@extend .well-sm;
|
||||
|
||||
margin-bottom: 10px;
|
||||
padding-top: 4px;
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.remove-button {
|
||||
@extend .btn;
|
||||
@extend .btn-danger;
|
||||
@extend .btn-sm;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
div.ingredient-editor {
|
||||
@include editor;
|
||||
|
||||
}
|
||||
|
||||
div#ingredient-list {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
div#ingredient-list {
|
||||
|
||||
@media (min-width: $screen-md-min) {
|
||||
.ingredient-editor .control-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ingredient-editor:first-child .control-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.recipe-view {
|
||||
|
||||
.source {
|
||||
@extend .col-xs-6;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.ingredients div {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.steps div {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
li.checked {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
|
||||
|
||||
span.star-rating {
|
||||
|
||||
display: inline-block;
|
||||
color: gold;
|
||||
cursor: default;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
|
||||
.empty-set {
|
||||
color: gray;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.filled-set {
|
||||
overflow-x: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
span.star {
|
||||
|
||||
@extend .glyphicon;
|
||||
|
||||
&.empty {
|
||||
@extend .glyphicon-star-empty;
|
||||
}
|
||||
|
||||
&.full {
|
||||
@extend .glyphicon-star;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
span.twitter-typeahead .tt-menu,
|
||||
span.twitter-typeahead .tt-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
float: left;
|
||||
min-width: 160px;
|
||||
padding: 5px 0;
|
||||
margin: 2px 0 0;
|
||||
list-style: none;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
span.twitter-typeahead .tt-hint {
|
||||
color: #A5A5A5;
|
||||
}
|
||||
|
||||
span.twitter-typeahead .tt-suggestion {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 1.42857143;
|
||||
color: #333333;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
span.twitter-typeahead .tt-suggestion.tt-cursor,
|
||||
span.twitter-typeahead .tt-suggestion:hover,
|
||||
span.twitter-typeahead .tt-suggestion:focus {
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
background-color: #337ab7;
|
||||
}
|
||||
|
||||
span.twitter-typeahead {
|
||||
width: 100%;
|
||||
}
|
||||
.input-group span.twitter-typeahead {
|
||||
display: block !important;
|
||||
}
|
||||
.input-group span.twitter-typeahead .tt-menu,
|
||||
.input-group span.twitter-typeahead .tt-dropdown-menu {
|
||||
top: 32px !important;
|
||||
}
|
||||
.input-group.input-group-sm span.twitter-typeahead .tt-menu,
|
||||
.input-group.input-group-sm span.twitter-typeahead .tt-dropdown-menu {
|
||||
top: 28px !important;
|
||||
}
|
||||
.input-group.input-group-lg span.twitter-typeahead .tt-menu,
|
||||
.input-group.input-group-lg span.twitter-typeahead .tt-dropdown-menu {
|
||||
top: 44px !important;
|
||||
}
|
@ -13,57 +13,4 @@ module ApplicationHelper
|
||||
decorated
|
||||
end
|
||||
|
||||
def timestamp(time)
|
||||
time ? time.strftime('%D %R') : ''
|
||||
end
|
||||
|
||||
def nav_items
|
||||
nav = [
|
||||
nav_item('Recipes', recipes_path, 'recipes'),
|
||||
nav_item('Ingredients', ingredients_path, 'ingredients'),
|
||||
nav_item('Logs', logs_path, 'logs', current_user?),
|
||||
nav_item('Calculator', calculator_path, 'calculator'),
|
||||
nav_item('About', about_path, 'home'),
|
||||
nav_item('Notes', notes_path, 'notes', current_user?),
|
||||
nav_item('Admin', admin_users_path, 'admin/users', current_user? && current_user.admin?)
|
||||
]
|
||||
|
||||
nav.compact
|
||||
end
|
||||
|
||||
def profile_nav_items
|
||||
if current_user
|
||||
[content_tag('li', class: 'dropdown') do
|
||||
li_cnt = ''.html_safe
|
||||
|
||||
li_cnt << link_to('#', class: 'dropdown-toggle', data: {toggle: 'dropdown'}, role: 'button') do
|
||||
''.html_safe << "#{current_user.display_name}" << content_tag('span', '', class: 'caret')
|
||||
end
|
||||
|
||||
li_cnt << content_tag('ul', class: 'dropdown-menu') do
|
||||
''.html_safe << nav_item('Profile', edit_user_path) << nav_item('Logout', logout_path)
|
||||
end
|
||||
|
||||
li_cnt
|
||||
end]
|
||||
else
|
||||
[nav_item('Login', login_path)]
|
||||
end
|
||||
end
|
||||
|
||||
def nav_item(name, url, controller = nil, visible = true)
|
||||
!visible ? nil : content_tag('li', link_to(name, url), class: active_for_controller(controller))
|
||||
end
|
||||
|
||||
def active_for_controller(controller)
|
||||
if current_controller == controller
|
||||
'active'
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
def current_controller
|
||||
params[:controller]
|
||||
end
|
||||
end
|
||||
|
@ -1,11 +1,4 @@
|
||||
module IngredientsHelper
|
||||
|
||||
def ndbn_button_class(ingredient)
|
||||
if ingredient.ndbn.present?
|
||||
'btn btn-success'
|
||||
else
|
||||
'btn btn-default'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,71 +1,3 @@
|
||||
module RecipesHelper
|
||||
def recipe_time(recipe)
|
||||
output = ''.html_safe
|
||||
|
||||
if recipe.total_time.present?
|
||||
output << "#{humanize_seconds(recipe.total_time.to_i.minutes)}"
|
||||
if recipe.active_time.present?
|
||||
output << " (#{humanize_seconds(recipe.active_time.to_i.minutes)} active)"
|
||||
end
|
||||
elsif recipe.active_time.present?
|
||||
output << humanize_seconds(recipe.active_time.to_i.minutes)
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
def humanize_seconds(secs)
|
||||
[[60, :s], [60, :m], [24, :h], [1000, :d]].map{ |count, name|
|
||||
if secs > 0
|
||||
secs, n = secs.divmod(count)
|
||||
n == 0 ? nil : "#{n.to_i} #{name}"
|
||||
end
|
||||
}.compact.reverse.join(' ')
|
||||
end
|
||||
|
||||
def nutrient_row(recipe, nutrients, heading, nutrient_name)
|
||||
content_tag('tr') do
|
||||
[
|
||||
content_tag('td', heading),
|
||||
recipe.parsed_yield ? content_tag('td', nutrients.send("#{nutrient_name}_per".to_sym, recipe.parsed_yield.number)) : nil,
|
||||
content_tag('td', nutrients.send("#{nutrient_name}".to_sym))
|
||||
].compact.join("\n".html_safe).html_safe
|
||||
end
|
||||
end
|
||||
|
||||
def index_sort_header(text, field, criteria)
|
||||
uri = URI(request.original_fullpath)
|
||||
query = Rack::Utils.parse_query(uri.query)
|
||||
|
||||
directions = [:asc, :desc]
|
||||
|
||||
current_field = criteria.sort_column
|
||||
current_direction = criteria.sort_direction
|
||||
field_param = 'criteria[sort_column]'
|
||||
direction_param = 'criteria[sort_direction]'
|
||||
|
||||
if request.get?
|
||||
is_sorted = current_field == field.to_sym
|
||||
|
||||
if is_sorted && directions.include?(current_direction)
|
||||
direction = (directions.reject { |d| d == current_direction }).first
|
||||
else
|
||||
direction = directions.first
|
||||
end
|
||||
|
||||
if is_sorted && direction == :asc
|
||||
link_class = 'sorted desc'
|
||||
elsif is_sorted && direction == :desc
|
||||
link_class = 'sorted asc'
|
||||
else
|
||||
link_class = 'sorted'
|
||||
end
|
||||
|
||||
query[field_param.to_s] = field.to_s
|
||||
query[direction_param.to_s] = direction.to_s
|
||||
link_to text, "#{uri.path}?#{query.to_query}", class: link_class
|
||||
else
|
||||
text
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,42 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="page-header">
|
||||
<h1>Users</h1>
|
||||
</div>
|
||||
|
||||
<% if @users.empty? %>
|
||||
<p>No Users</p>
|
||||
<% else %>
|
||||
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Username</th>
|
||||
<th>Email</th>
|
||||
<th>Admin</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% @users.each do |user| %>
|
||||
<tr>
|
||||
<td><%= link_to user.full_name, edit_admin_user_path(user) %></td>
|
||||
<td><%= user.username %></td>
|
||||
<td><%= user.email %></td>
|
||||
<td><%= user.admin? %></td>
|
||||
<td>
|
||||
<%= link_to [:admin, user], method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-sm btn-danger' do %>
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,49 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<div id="calculator" class="page-header">
|
||||
<h1>Calculator</h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group col-xs-12 col-sm-8">
|
||||
<%= label_tag :input, "Input", class: 'control-label' %>
|
||||
<%= text_field_tag :input, nil, class: 'form-control', autofocus: true %>
|
||||
</div>
|
||||
<div class="form-group col-xs-12 col-sm-4">
|
||||
<%= label_tag :output_unit, "Output Unit", class: 'control-label' %>
|
||||
<%= text_field_tag :output_unit, nil, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-6">
|
||||
<%= label_tag :ingredient, "Ingredient", class: 'control-label' %>
|
||||
<%= text_field_tag :ingredient, nil, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-6">
|
||||
<%= label_tag :density, "Density", class: 'control-label' %>
|
||||
<%= text_field_tag :density, nil, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-12">
|
||||
<%= label_tag :output, "Output", class: 'control-label' %>
|
||||
<%= text_field_tag :output, nil, class: 'form-control', readonly: true %>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div id="errors_panel" class="panel panel-danger" style="display: none;">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Error</h3>
|
||||
</div>
|
||||
<div id="errors_container" class="panel-body">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,151 +0,0 @@
|
||||
|
||||
<% has_ndbn = @ingredient.ndbn.present? %>
|
||||
|
||||
<%= form_for(@ingredient, html: {id: 'ingredient_form'}) do |f| %>
|
||||
|
||||
<%= render partial: 'shared/error_list', locals: {model: @ingredient} %>
|
||||
|
||||
<%= f.hidden_field :ndbn, class: 'ndbn' %>
|
||||
<%= f.hidden_field :id, class: 'id', disabled: true %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :name, class: 'control-label' %>
|
||||
<%= f.text_field :name, class: 'form-control name', autofocus: true %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :ndbn, "Nutrient Databank Number", class: 'control-label' %>
|
||||
<div class="input-group">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="<%= ndbn_button_class(@ingredient) %>">
|
||||
<span class="glyphicon glyphicon-link"></span><span class="ndbn"><%= @ingredient.ndbn %></span>
|
||||
</button>
|
||||
</span>
|
||||
<input type="text" class="ndbn_typeahead form-control" value="<%= @ingredient.usda_food_name %>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ndbn_results">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :density, class: 'control-label' %>
|
||||
<%= f.text_field :density, class: 'form-control', disabled: has_ndbn %>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Ingredient Units</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="ingredient-unit-list">
|
||||
<%= f.fields_for :ingredient_units do |iu_form| %>
|
||||
<%= render partial: 'ingredients/ingredient_unit_fields', locals: {f: iu_form } %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to_add_association 'add unit', f, :ingredient_units, class: 'btn btn-primary' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if has_ndbn %>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">NDBN Unit Weights</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Grams</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<% @ingredient.usda_food.usda_food_weights.each do |w| %>
|
||||
<tr>
|
||||
<td><%= "#{w.amount} #{w.description}" %></td>
|
||||
<td><%= w.gram_weight %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :notes, class: 'control-label' %>
|
||||
<%= f.text_area :notes, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Nutrition Per 100 grams</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<fieldset <%= 'disabled=disabled' if has_ndbn %>>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<div class="form-group">
|
||||
<%= f.label :water, "Grams of Water", class: 'control-label' %>
|
||||
<%= f.text_field :water, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :protein, "Grams of Protein", class: 'control-label' %>
|
||||
<%= f.text_field :protein, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :lipids, "Grams of Fat", class: 'control-label' %>
|
||||
<%= f.text_field :lipids, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :carbohydrates, "Grams of Carbohydrates", class: 'control-label' %>
|
||||
<%= f.text_field :carbohydrates, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :kcal, "Calories", class: 'control-label' %>
|
||||
<%= f.text_field :kcal, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :fiber, "Grams of Fiber", class: 'control-label' %>
|
||||
<%= f.text_field :fiber, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :sugar, "Grams of Sugar", class: 'control-label' %>
|
||||
<%= f.text_field :sugar, class: 'form-control' %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<div class="form-group">
|
||||
<%= f.label :calcium, "Miligrams of Calcium", class: 'control-label' %>
|
||||
<%= f.text_field :calcium, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :sodium, "Miligrams of Sodium", class: 'control-label' %>
|
||||
<%= f.text_field :sodium, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :vit_k, "Micrograms of Vitamin K", class: 'control-label' %>
|
||||
<%= f.text_field :vit_k, class: 'form-control' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<%= f.submit class: 'btn btn-primary' %>
|
||||
</div>
|
||||
|
||||
<% end %>
|
@ -1,22 +0,0 @@
|
||||
<div class="nested-fields row">
|
||||
|
||||
<div class="form-group col-xs-5 col-sm-6">
|
||||
<%= f.label :name, class: 'control-label' %>
|
||||
<%= f.text_field :name, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-4 col-sm-5">
|
||||
<%= f.label :gram_weight, 'Grams', class: 'control-label' %>
|
||||
<%= f.text_field :gram_weight, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-3 col-sm-1">
|
||||
<label class="control-label"></label>
|
||||
<p class="form-control-static">
|
||||
<%= link_to_remove_association f, class: 'btn btn-danger btn-sm' do %>
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
<% end %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
@ -1,11 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<h1>Editing Ingredient</h1>
|
||||
|
||||
<%= render 'form' %>
|
||||
|
||||
<%= link_to 'Back', ingredients_path, class: 'btn btn-default' %>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,58 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="page-header">
|
||||
<h1>Ingredients</h1>
|
||||
</div>
|
||||
|
||||
<% if @ingredients.empty? %>
|
||||
<p>No Ingredients</p>
|
||||
<% else %>
|
||||
|
||||
<% if current_user? %>
|
||||
<%= link_to 'New Ingredient', new_ingredient_path, class: 'btn btn-default' %>
|
||||
<% end %>
|
||||
|
||||
<br/>
|
||||
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>USDA</th>
|
||||
<th>KCal per 100g</th>
|
||||
<th>Density (oz/cup)</th>
|
||||
<% if current_user? %>
|
||||
<th></th>
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% decorate(@ingredients, IngredientDecorator).each do |ingredient| %>
|
||||
<tr>
|
||||
<td><%= link_to_if current_user?, ingredient.name, edit_ingredient_path(ingredient) %></td>
|
||||
<td><%= ingredient.ndbn_check %></td>
|
||||
<td><%= ingredient.kcal %></td>
|
||||
<td><%= ingredient.density %></td>
|
||||
<% if current_user? %>
|
||||
<td>
|
||||
<%= link_to ingredient, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-xs btn-danger' do %>
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
<% end %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
|
||||
<br/>
|
||||
|
||||
<% if current_user? %>
|
||||
<%= link_to 'New Ingredient', new_ingredient_path, class: 'btn btn-default' %>
|
||||
<% end %>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,11 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<h1>New Ingredient</h1>
|
||||
|
||||
<%= render 'form' %>
|
||||
|
||||
<%= link_to 'Back', ingredients_path, class: 'btn btn-primary' %>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,3 +0,0 @@
|
||||
|
||||
$("#ingredient_form").replaceWith($("<%= escape_javascript(render(partial: 'ingredients/form')) %>"));
|
||||
window.INGREDIENT_API.initialize();
|
@ -1,30 +0,0 @@
|
||||
|
||||
<table class="table table-striped table-hover table-condensed">
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2"></th>
|
||||
<th colspan="3">per 100 grams</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>NDBN</th>
|
||||
<th>Name</th>
|
||||
<th>KCal</th>
|
||||
<th>Carbs</th>
|
||||
<th>Sugar</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<% @foods.each do |f| %>
|
||||
|
||||
<tr>
|
||||
<td><%= f.ndbn %></td>
|
||||
<td><%= link_to f.long_description, '#', class: 'food_result', data: {ndbn: f.ndbn} %></td>
|
||||
<td><%= f.kcal %></td>
|
||||
<td><%= f.carbohydrates %></td>
|
||||
<td><%= f.sugar %></td>
|
||||
</tr>
|
||||
|
||||
<% end %>
|
||||
|
||||
</table>
|
@ -1,3 +0,0 @@
|
||||
<li>
|
||||
<%= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote %>
|
||||
</li>
|
@ -1,3 +0,0 @@
|
||||
<li class='disabled'>
|
||||
<%= content_tag :a, raw(t 'views.pagination.truncate') %>
|
||||
</li>
|
@ -1,3 +0,0 @@
|
||||
<li>
|
||||
<%= link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote} %>
|
||||
</li>
|
@ -1,3 +0,0 @@
|
||||
<li>
|
||||
<%= link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, :rel => 'next', :remote => remote %>
|
||||
</li>
|
@ -1,9 +0,0 @@
|
||||
<% if page.current? %>
|
||||
<li class='active'>
|
||||
<%= content_tag :a, page, remote: remote, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) %>
|
||||
</li>
|
||||
<% else %>
|
||||
<li>
|
||||
<%= link_to page, url, remote: remote, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) %>
|
||||
</li>
|
||||
<% end %>
|
@ -1,15 +0,0 @@
|
||||
<%= paginator.render do -%>
|
||||
<ul class="pagination">
|
||||
<%= first_page_tag unless current_page.first? %>
|
||||
<%= prev_page_tag unless current_page.first? %>
|
||||
<% each_page do |page| -%>
|
||||
<% if page.left_outer? || page.right_outer? || page.inside_window? -%>
|
||||
<%= page_tag page %>
|
||||
<% elsif !page.was_truncated? -%>
|
||||
<%= gap_tag %>
|
||||
<% end -%>
|
||||
<% end -%>
|
||||
<%= next_page_tag unless current_page.last? %>
|
||||
<%= last_page_tag unless current_page.last? %>
|
||||
</ul>
|
||||
<% end -%>
|
@ -1,3 +0,0 @@
|
||||
<li>
|
||||
<%= link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, :rel => 'prev', :remote => remote %>
|
||||
</li>
|
@ -1,10 +0,0 @@
|
||||
<div id="flashContainer"></div>
|
||||
|
||||
<div id="flashHolder">
|
||||
<% flash.each do |type, values| %>
|
||||
<% Array.wrap(values).each do |value| %>
|
||||
<div class="<%= type %>"><%= value %></div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
@ -9,40 +9,11 @@
|
||||
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
|
||||
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
|
||||
|
||||
<link rel="apple-touch-icon-precomposed" href="<%= image_path('favicon/tree-14a-152-195103.png') %>">
|
||||
<link rel="icon" href="<%= image_path('favicon/tree-14a-152-195103.png') %>" sizes="152x152">
|
||||
|
||||
<%= csrf_meta_tags %>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-inverse">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/">Parsley</a>
|
||||
</div>
|
||||
<div id="navbar" class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<% nav_items.each do |li| %>
|
||||
<%= li %>
|
||||
<% end %>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<% profile_nav_items.each do |li| %>
|
||||
<%= li %>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<%= render partial: 'layouts/flash_messages' %>
|
||||
|
||||
<div class="container" id="main_container">
|
||||
|
||||
@ -50,18 +21,6 @@
|
||||
|
||||
</div><!-- /.container -->
|
||||
|
||||
<!--<footer class="footer">-->
|
||||
<!--<div class="container-fluid">-->
|
||||
<!--<div class="row">-->
|
||||
<!--<div class="col-xs-12">-->
|
||||
<!--<p class="text-muted text-right">© Dan Elbert 2016</p>-->
|
||||
<!--</div>-->
|
||||
<!--</div>-->
|
||||
<!--</div>-->
|
||||
<!--</footer>-->
|
||||
|
||||
<%= yield(:page_bottom) %>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,46 +0,0 @@
|
||||
<% @log = decorate(@log, LogDecorator) %>
|
||||
|
||||
<%= form_for([@recipe, @log], html: {class: 'log-form'}) do |f| %>
|
||||
|
||||
<%= render partial: 'shared/error_list', locals: {model: @log} %>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :date, class: 'control-label' %>
|
||||
<%= f.text_field :date, class: 'form-control datepicker', value: @log.date %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :rating, class: 'control-label' %>
|
||||
<%= f.text_field :rating, class: 'form-control', data: {rating: 'true'} %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :notes, 'Log Notes', class: 'control-label' %>
|
||||
<%= f.text_area :notes, class: 'form-control' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12">
|
||||
<div class="actions">
|
||||
<%= f.submit class: 'btn btn-primary' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= f.fields_for(:recipe, include_id: false) do |rf| %>
|
||||
<%= render partial: 'recipes/editor', locals: {f: rf} %>
|
||||
<% end %>
|
||||
<br/><br/>
|
||||
|
||||
<div class="actions">
|
||||
<%= f.submit class: 'btn btn-primary' %>
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
|
@ -1,12 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="page-header">
|
||||
<h1>Edit Log Entry</h1>
|
||||
</div>
|
||||
|
||||
<%= render 'form' %>
|
||||
|
||||
<%= link_to 'Back', logs_path, class: 'btn btn-default' %>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,40 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Log Entries</h1>
|
||||
</div>
|
||||
|
||||
<% if @logs.empty? %>
|
||||
<p>No Entries</p>
|
||||
<% else %>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="log-table table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Recipe</th>
|
||||
<th>Date</th>
|
||||
<th>Rating</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% decorate(@logs, LogDecorator).each do |log| %>
|
||||
<tr>
|
||||
<td><%= link_to log.recipe.short_name, log %></td>
|
||||
<td><%= log.date %></td>
|
||||
<td><%= text_field_tag('rating', log.rating, disabled: true, data: {rating: true}) %></td>
|
||||
<td><%= log.notes %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,12 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="page-header">
|
||||
<h1>Log Recipe</h1>
|
||||
</div>
|
||||
|
||||
<%= render 'form' %>
|
||||
|
||||
<%= link_to 'Back', recipes_path, class: 'btn btn-default' %>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,92 +0,0 @@
|
||||
|
||||
<%
|
||||
@log = decorate(@log, LogDecorator)
|
||||
@recipe = @log.recipe
|
||||
%>
|
||||
|
||||
<div class="recipe-view">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
[Log Entry] <%= @recipe.name %>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<p>Date: <%= @log.date %></p>
|
||||
<p>Rating: <%= text_field_tag('rating', @log.rating, disabled: true, data: {rating: true}) %></p>
|
||||
<p>Notes: <%= @log.notes %></p>
|
||||
|
||||
<p class="lead">
|
||||
<%= @recipe.description %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<% if @recipe.total_time.present? || @recipe.active_time.present? %>
|
||||
<div class="col-xs-3">
|
||||
<p><%= recipe_time(@recipe) %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if @recipe.yields.present? %>
|
||||
<div class="col-xs-3">
|
||||
<p>Yields</p><p><%= @recipe.yields %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if @recipe.source.present? %>
|
||||
<div class="source">
|
||||
<p>Source</p><p><%= @recipe.source_markup %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-5 col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
|
||||
<div class="row">
|
||||
<h3 class="panel-title col-xs-7">Ingredients</h3>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<ul class="ingredients">
|
||||
<% @recipe.recipe_ingredients.each do |i| %>
|
||||
<li><div><%= i.display_name %></div></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-7 col-md-8">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Directions</h3>
|
||||
</div>
|
||||
<div class="panel-body steps">
|
||||
<%= @recipe.step_text %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<%= link_to 'Edit', edit_log_path(@log), class: 'btn btn-default' %>
|
||||
<%= link_to 'Back', logs_path, class: 'btn btn-default' %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,13 +0,0 @@
|
||||
<%= form_for(note) do |f| %>
|
||||
<%= render partial: 'shared/error_list', locals: {model: @note} %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :content, 'Note', class: 'control-label' %>
|
||||
<%= f.text_field :content, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<%= f.submit class: 'btn btn-primary' %>
|
||||
<%= link_to 'Back', notes_path, class: 'btn btn-default' %>
|
||||
</div>
|
||||
<% end %>
|
@ -1,11 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="page-header">
|
||||
<h1>Edit Note</h1>
|
||||
</div>
|
||||
|
||||
<%= render 'form', note: @note %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,47 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="page-header">
|
||||
<h1>Notes</h1>
|
||||
</div>
|
||||
|
||||
<% if @notes.empty? %>
|
||||
<p>No Notes</p>
|
||||
<% else %>
|
||||
|
||||
<%= link_to 'New Note', new_note_path, class: 'btn btn-default' %>
|
||||
|
||||
<br/>
|
||||
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Note</th>
|
||||
<th>Date</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% decorate(@notes, NoteDecorator).each do |note| %>
|
||||
<tr>
|
||||
<td><%= note.content %></td>
|
||||
<td><%= note.created_at %></td>
|
||||
<td>
|
||||
<%= link_to note, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-xs btn-danger' do %>
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
|
||||
<br/>
|
||||
|
||||
<%= link_to 'New Note', new_note_path, class: 'btn btn-default' %>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,11 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="page-header">
|
||||
<h1>Create Note</h1>
|
||||
</div>
|
||||
|
||||
<%= render 'form', note: @note %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,15 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="page-header">
|
||||
<h1>Note</h1>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<%= @note.content %>
|
||||
</p>
|
||||
|
||||
<%= link_to 'Back', notes_path, class: 'btn btn-default' %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,101 +0,0 @@
|
||||
|
||||
<div class="recipe_editor">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-7">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :name, class: "control-label" %>
|
||||
<%= f.text_field :name, class: 'form-control' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-5">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :source, class: "control-label" %>
|
||||
<%= f.text_field :source, class: 'form-control' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :description, class: "control-label" %>
|
||||
<%= f.text_area :description, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group form-group-sm tag_names_container">
|
||||
<%= f.label :tag_names, class: "control-label" %>
|
||||
<%= f.select :tag_names, options_for_select(f.object.tag_names), {}, multiple: true, class: 'form-control tag_names' %>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :yields, class: "control-label" %>
|
||||
<%= f.text_field :yields, class: 'form-control', placeholder: 'Servings' %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :total_time, class: "control-label" %>
|
||||
<%= f.number_field :total_time, class: 'form-control', placeholder: 'Minutes' %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :active_time, class: "control-label" %>
|
||||
<%= f.number_field :active_time, class: 'form-control', placeholder: 'Minutes' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h3>Ingredients</h3>
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#bulk_ingredients_modal">Bulk Edit</button>
|
||||
<div id="ingredient-list">
|
||||
<%= f.fields_for :recipe_ingredients do |ri_form| %>
|
||||
<%= render partial: 'recipes/editor/ingredient', locals: { f: ri_form } %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div id="deleted_ingredients"></div>
|
||||
|
||||
<%= link_to_add_association 'Add Ingredient', f, :recipe_ingredients, partial: 'recipes/editor/ingredient', :'data-association-insertion-node' => '#ingredient-list', :'data-association-insertion-method' => 'append', class: 'btn btn-primary', id: 'addIngredientButton' %>
|
||||
|
||||
|
||||
<h3>Steps</h3>
|
||||
<div class="steps">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.text_area :step_text, class: 'form-control', rows: 15 %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% content_for(:page_bottom) do %>
|
||||
|
||||
<%= render partial: 'recipes/editor/bulk_ingredient_dialog' %>
|
||||
|
||||
<div class="modal fade" id="convert_modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Unit Conversion</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<%= render partial: 'recipes/editor/conversion_form' %>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary" form="conversion_form">Convert</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
|
||||
</div>
|
@ -1,11 +0,0 @@
|
||||
<%= form_for(@recipe, {html: {class: 'recipe-form'}}) do |f| %>
|
||||
|
||||
<%= render partial: 'shared/error_list', locals: {model: @recipe} %>
|
||||
|
||||
<%= render partial: 'editor', locals: {f: f} %>
|
||||
<br/><br/>
|
||||
|
||||
<div class="actions">
|
||||
<%= f.submit class: 'btn btn-primary' %>
|
||||
</div>
|
||||
<% end %>
|
@ -1,12 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<h1>Editing Recipe</h1>
|
||||
|
||||
<%= render 'form' %>
|
||||
|
||||
<%= link_to 'Show', @recipe, class: 'btn btn-primary' %> |
|
||||
<%= link_to 'Back', recipes_path, class: 'btn btn-primary' %>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,48 +0,0 @@
|
||||
<div class="modal fade" id="bulk_ingredients_modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Add Ingredients</h4>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group form-group-lg">
|
||||
<label for="ingredient_bulk_input" class="control-label">Raw Input</label>
|
||||
<textarea id="ingredient_bulk_input" class="form-control"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-6">
|
||||
<table class="table table-condensed table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Unit</th>
|
||||
<th>Name</th>
|
||||
<th>Prep</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="ingredient_bulk_parsed_list">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" id="bulkIngredientAddSubmit">Add</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,46 +0,0 @@
|
||||
<div class="modal fade" id="bulk_steps_modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Add Steps</h4>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label for="step_bulk_input" class="control-label">Raw Input</label>
|
||||
<textarea id="step_bulk_input" class="form-control"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-6">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Direction</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="step_bulk_parsed_list">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" id="bulkStepAddSubmit">Add</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,48 +0,0 @@
|
||||
|
||||
<div id="modal_form_container">
|
||||
|
||||
<%= form_for :conversion, url: convert_ingredients_path, remote: true, method: :get, html: { id: 'conversion_form' } do |f| %>
|
||||
|
||||
<% if @conversion %>
|
||||
<%= render partial: 'shared/error_list', locals: {model: @conversion} %>
|
||||
<% end %>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :input_quantity, "Quantity", class: 'control-label' %>
|
||||
<%= f.text_field :input_quantity, class: 'form-control quantity' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :input_units, "Units", class: 'control-label' %>
|
||||
<%= f.text_field :input_units, class: 'form-control units' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :scale, class: 'control-label' %>
|
||||
<%= f.text_field :scale, class: 'form-control scale' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :output_units, class: 'control-label' %>
|
||||
<%= f.text_field :output_units, class: 'form-control output_units' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= f.hidden_field :ingredient_id, class: 'ingredient_id' %>
|
||||
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -1,57 +0,0 @@
|
||||
<div class="nested-fields ingredient-editor">
|
||||
<!--<div class="panel-body">-->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-10 col-sm-11">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-xs-6 col-sm-6 col-md-2">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :quantity, class: "control-label" %>
|
||||
<%= f.text_field :quantity, class: 'form-control quantity' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-6 col-sm-6 col-md-3">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :units, class: "control-label" %>
|
||||
<div class="input-group input-group-sm">
|
||||
<%= f.text_field :units, class: 'form-control units' %>
|
||||
<span class="input-group-btn">
|
||||
<button tabindex="-1" class="btn btn-info ingredient_convert_btn" type="button" data-toggle="modal" data-target="#convert_modal">
|
||||
<span class="glyphicon glyphicon-transfer"></span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-6 col-md-3">
|
||||
<div class="form-group form-group-sm typeahead-group">
|
||||
<%= f.label :name, class: "control-label" %>
|
||||
<%= f.text_field :name, class: 'form-control ingredient-typeahead custom' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-6 col-md-4">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.label :preparation, class: "control-label" %>
|
||||
<%= f.text_field :preparation, class: 'form-control preparation' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-2 col-sm-1">
|
||||
<%= link_to_remove_association f, class: 'remove-button' do %>
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= f.hidden_field :ingredient_id, class: 'ingredient_id' %>
|
||||
<%= f.hidden_field :sort_order, class: 'sort_order' %>
|
||||
<!--</div>-->
|
||||
</div>
|
@ -1,25 +0,0 @@
|
||||
<div class="well well-sm nested-fields step-editor">
|
||||
<!--<div class="panel-body">-->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-2 col-sm-1">
|
||||
<span class="sort-order-display"><%= f.object ? f.object.sort_order : '' %></span>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-8 col-sm-10">
|
||||
<div class="form-group form-group-sm">
|
||||
<%= f.text_area :step, class: 'form-control step' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-2 col-sm-1">
|
||||
<%= link_to_remove_association f, class: 'remove-button' do %>
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= f.hidden_field :sort_order, class: 'sort_order' %>
|
||||
|
||||
<!--</div>-->
|
||||
</div>
|
@ -1,96 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Recipes</h1>
|
||||
</div>
|
||||
|
||||
<% if current_user? %>
|
||||
<%= link_to 'New Recipe', new_recipe_path, class: 'btn btn-default' %><br/>
|
||||
<% end %>
|
||||
|
||||
<%= paginate @recipes %>
|
||||
|
||||
<%= form_for(@criteria, as: :criteria, url: recipes_path, method: :get, html: {id: 'search_form'}) do |f| %>
|
||||
<%= f.hidden_field :sort_column %>
|
||||
<%= f.hidden_field :sort_direction %>
|
||||
<%= f.hidden_field :page %>
|
||||
<%= f.hidden_field :per %>
|
||||
<%= f.hidden_field :name %>
|
||||
<%= f.hidden_field :tags %>
|
||||
<% end %>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="recipe-table table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= index_sort_header('Name', :name, @criteria) %></th>
|
||||
<th>Tags</th>
|
||||
<th><%= index_sort_header('Rating', :rating, @criteria) %></th>
|
||||
<th>Yields</th>
|
||||
<th><%= index_sort_header('Time', :total_time, @criteria) %></th>
|
||||
<th><%= index_sort_header('Created', :created_at, @criteria) %></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= text_field_tag('name_search', @criteria.name) %></th>
|
||||
<th><%= text_field_tag('tags_search', @criteria.tags) %></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th><button id="recipe_index_search_button" class="btn btn-sm btn-default">Search</button></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% decorate(@recipes, RecipeDecorator).each do |recipe| %>
|
||||
<tr>
|
||||
<td><%= link_to recipe.short_name, recipe %></td>
|
||||
<td><div class="table_tags"><%= recipe.tag_names %></div></td>
|
||||
<td>
|
||||
<% if recipe.rating %>
|
||||
<%= text_field_tag('rating', recipe.rating, disabled: true, data: {rating: true, size: '20px', interval: '0.25'}) %>
|
||||
<% else %>
|
||||
--
|
||||
<% end %>
|
||||
</td>
|
||||
<td><%= recipe.yields %></td>
|
||||
<td><%= recipe_time(recipe) %></td>
|
||||
<td><%= recipe.created_at %></td>
|
||||
|
||||
<td>
|
||||
<% if current_user? %>
|
||||
<div class="recipe_list_controls">
|
||||
<%= link_to new_recipe_log_path(recipe), class: 'btn btn-xs btn-primary' do %>
|
||||
<span class="glyphicon glyphicon-copy"></span>
|
||||
<% end %>
|
||||
<%= link_to edit_recipe_path(recipe), class: 'btn btn-xs btn-primary' do %>
|
||||
<span class="glyphicon glyphicon-pencil"></span>
|
||||
<% end %>
|
||||
<%= link_to recipe, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-xs btn-danger' do %>
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<% if @recipes.empty? %>
|
||||
<p>No Recipes</p>
|
||||
<% end %>
|
||||
|
||||
<%= paginate @recipes %>
|
||||
|
||||
<br>
|
||||
|
||||
<% if current_user? %>
|
||||
<%= link_to 'New Recipe', new_recipe_path, class: 'btn btn-default' %><br/>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,12 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="page-header">
|
||||
<h1>New Recipe</h1>
|
||||
</div>
|
||||
|
||||
<%= render 'form' %>
|
||||
|
||||
<%= link_to 'Back', recipes_path, class: 'btn btn-default' %>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,203 +0,0 @@
|
||||
|
||||
<div class="recipe-view">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
<%= @recipe.name %>
|
||||
<% if @scale %>
|
||||
<span class="label label-default"><%= @scale %> X</span>
|
||||
<% end %>
|
||||
<br/>
|
||||
<small><%= @recipe.tag_names %></small>
|
||||
</h1>
|
||||
</div>
|
||||
<p class="lead">
|
||||
<%= @recipe.description %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<% if @recipe.total_time.present? || @recipe.active_time.present? %>
|
||||
<div class="col-xs-3">
|
||||
<p><%= recipe_time(@recipe) %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if @recipe.yields.present? %>
|
||||
<div class="col-xs-3">
|
||||
<p>Yields</p><p><%= @recipe.yields %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if @recipe.source.present? %>
|
||||
<div class="source">
|
||||
<p>Source</p><p><%= @recipe.source_markup %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-5 col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
|
||||
<div class="row">
|
||||
<h3 class="panel-title col-xs-7">Ingredients</h3>
|
||||
<div class="dropdown col-xs-5">
|
||||
<button id="scaleLabel" type="button" class="pull-right btn btn-xs btn-default" data-toggle="modal" data-target="#translate_modal">
|
||||
Translate
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<ul class="ingredients">
|
||||
<% @recipe.recipe_ingredients.each do |i| %>
|
||||
<li><div><%= i.display_name %></div></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-7 col-md-8">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Directions</h3>
|
||||
</div>
|
||||
<div class="panel-body steps">
|
||||
<%= @recipe.step_text %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><a href="#nutrition_panel" data-toggle="collapse">Nutrition Data</a></h3>
|
||||
</div>
|
||||
<div id="nutrition_panel" class="panel-body collapse">
|
||||
<% decorate(@recipe.nutrition_data, NutritionDataDecorator) do |nutrition_data| %>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<% if @recipe.parsed_yield %>
|
||||
<th><%= @recipe.parsed_yield.label %></th>
|
||||
<% end %>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<% NutritionData::NUTRIENTS.select { |_, v| v.present? }.each do |k, v| %>
|
||||
<%= nutrient_row(@recipe, nutrition_data, v, k) %>
|
||||
<% end %>
|
||||
</table>
|
||||
<% end %>
|
||||
|
||||
<h3>Nutrition Calculation Warnings</h3>
|
||||
|
||||
<ul>
|
||||
<% @recipe.nutrition_data.errors.each do |err| %>
|
||||
<li><%= err %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<%= link_to 'Edit', edit_recipe_path(@recipe), class: 'btn btn-default' %>
|
||||
<%= link_to 'Back', recipes_path, class: 'btn btn-default' %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="translate_modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Recipe Translation</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<%= form_tag(recipe_path(@recipe), method: :get, authenticity_token: false, enforce_utf8: false, id: 'translate_form') do %>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<div class="form-group form-group">
|
||||
<%= label_tag :scale, 'Scale', class: "control-label" %>
|
||||
<%= select_tag :scale, options_for_select(['1/4', '1/3', '1/2', '2/3', '3/4', '1', '1 1/2', '2', '3', '4'], '1'), class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="well">
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="system" id="system_none" value="" checked>
|
||||
No System Translation
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="system" id="system_standard" value="standard">
|
||||
Convert to Standard Units
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="system" id="system_metric" value="metric">
|
||||
Convert to Metric Units
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="well">
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="unit" id="unit_none" value="" checked>
|
||||
No Unit Translation
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="unit" id="unit_mass" value="mass">
|
||||
Convert to Mass Units
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="unit" id="unit_volume" value="volume">
|
||||
Convert to Volume Units
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary" form="translate_form">Convert</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,11 +0,0 @@
|
||||
<% if model.errors.any? %>
|
||||
<div id="error_explanation" class="alert alert-danger">
|
||||
<h4><%= pluralize(model.errors.count, 'error') %> prohibited this <%= model.model_name.human %> from being saved:</h4>
|
||||
|
||||
<ul>
|
||||
<% model.errors.full_messages.each do |msg| %>
|
||||
<li><%= msg %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
@ -1,30 +0,0 @@
|
||||
<%= form_for(@user, url: user_path) do |f| %>
|
||||
|
||||
<%= render partial: 'shared/error_list', locals: {model: @user} %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :username, class: 'control-label' %>
|
||||
<%= f.text_field :username, autofocus: true, class: 'form-control' %>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<%= f.label :full_name, 'Name', class: 'control-label' %>
|
||||
<%= f.text_field :full_name, class: 'form-control' %>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<%= f.label :email, class: 'control-label' %>
|
||||
<%= f.text_field :email, class: 'form-control' %>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<%= f.label :password, class: 'control-label' %>
|
||||
<%= f.password_field :password, class: 'form-control' %>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<%= f.label :password_confirmation, class: 'control-label' %>
|
||||
<%= f.password_field :password_confirmation, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<%= f.submit class: 'btn btn-primary' %>
|
||||
<%= link_to "Back", root_path, class: 'btn btn-default' %>
|
||||
</div>
|
||||
<% end %>
|
@ -1,11 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Edit Your Profile</h1>
|
||||
</div>
|
||||
|
||||
<%= render partial: 'form' %>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,31 +0,0 @@
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Parsley Login</h1>
|
||||
</div>
|
||||
|
||||
|
||||
<%= form_tag(login_path, :method => :post) do %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= label_tag :username, "Username", class: 'control-label' %>
|
||||
<%= text_field_tag :username, nil, autofocus: true, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= label_tag :password, "Password", class: 'control-label' %>
|
||||
<%= password_field_tag :password, nil, class: 'form-control' %>
|
||||
</div>
|
||||
|
||||
<%= submit_tag("Login", class: 'btn btn-primary') %>
|
||||
<% end %>
|
||||
|
||||
<br />
|
||||
|
||||
<%= link_to "Create an Account", new_user_path, class: 'btn btn-default' %>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,11 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Create user</h1>
|
||||
</div>
|
||||
|
||||
<%= render partial: 'form' %>
|
||||
|
||||
</div>
|
||||
</div>
|
BIN
vendor/assets/fonts/inconsolata-bold.woff2
vendored
BIN
vendor/assets/fonts/inconsolata-bold.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/inconsolata.woff2
vendored
BIN
vendor/assets/fonts/inconsolata.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/news-cycle-bold.woff2
vendored
BIN
vendor/assets/fonts/news-cycle-bold.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/news-cycle.woff2
vendored
BIN
vendor/assets/fonts/news-cycle.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/open-sans-bold-italic.woff2
vendored
BIN
vendor/assets/fonts/open-sans-bold-italic.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/open-sans-bold.woff2
vendored
BIN
vendor/assets/fonts/open-sans-bold.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/open-sans-italic.woff2
vendored
BIN
vendor/assets/fonts/open-sans-italic.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/open-sans-light-italic.woff2
vendored
BIN
vendor/assets/fonts/open-sans-light-italic.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/open-sans-light.woff2
vendored
BIN
vendor/assets/fonts/open-sans-light.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/open-sans.woff2
vendored
BIN
vendor/assets/fonts/open-sans.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/raleway-bold.woff2
vendored
BIN
vendor/assets/fonts/raleway-bold.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/raleway.woff2
vendored
BIN
vendor/assets/fonts/raleway.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/roboto-bold.woff2
vendored
BIN
vendor/assets/fonts/roboto-bold.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/roboto-light.woff2
vendored
BIN
vendor/assets/fonts/roboto-light.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/roboto-medium.woff2
vendored
BIN
vendor/assets/fonts/roboto-medium.woff2
vendored
Binary file not shown.
BIN
vendor/assets/fonts/roboto.woff2
vendored
BIN
vendor/assets/fonts/roboto.woff2
vendored
Binary file not shown.
BIN
vendor/assets/images/chosen-sprite.png
vendored
BIN
vendor/assets/images/chosen-sprite.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 538 B |
BIN
vendor/assets/images/chosen-sprite@2x.png
vendored
BIN
vendor/assets/images/chosen-sprite@2x.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 738 B |
0
vendor/assets/javascripts/.keep
vendored
0
vendor/assets/javascripts/.keep
vendored
243
vendor/assets/javascripts/autosize.js
vendored
243
vendor/assets/javascripts/autosize.js
vendored
@ -1,243 +0,0 @@
|
||||
/*!
|
||||
Autosize 3.0.14
|
||||
license: MIT
|
||||
http://www.jacklmoore.com/autosize
|
||||
*/
|
||||
(function (global, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(['exports', 'module'], factory);
|
||||
} else if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
|
||||
factory(exports, module);
|
||||
} else {
|
||||
var mod = {
|
||||
exports: {}
|
||||
};
|
||||
factory(mod.exports, mod);
|
||||
global.autosize = mod.exports;
|
||||
}
|
||||
})(this, function (exports, module) {
|
||||
'use strict';
|
||||
|
||||
var set = typeof Set === 'function' ? new Set() : (function () {
|
||||
var list = [];
|
||||
|
||||
return {
|
||||
has: function has(key) {
|
||||
return Boolean(list.indexOf(key) > -1);
|
||||
},
|
||||
add: function add(key) {
|
||||
list.push(key);
|
||||
},
|
||||
'delete': function _delete(key) {
|
||||
list.splice(list.indexOf(key), 1);
|
||||
} };
|
||||
})();
|
||||
|
||||
function assign(ta) {
|
||||
var _ref = arguments[1] === undefined ? {} : arguments[1];
|
||||
|
||||
var _ref$setOverflowX = _ref.setOverflowX;
|
||||
var setOverflowX = _ref$setOverflowX === undefined ? true : _ref$setOverflowX;
|
||||
var _ref$setOverflowY = _ref.setOverflowY;
|
||||
var setOverflowY = _ref$setOverflowY === undefined ? true : _ref$setOverflowY;
|
||||
|
||||
if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || set.has(ta)) return;
|
||||
|
||||
var heightOffset = null;
|
||||
var overflowY = null;
|
||||
var clientWidth = ta.clientWidth;
|
||||
|
||||
function init() {
|
||||
var style = window.getComputedStyle(ta, null);
|
||||
|
||||
overflowY = style.overflowY;
|
||||
|
||||
if (style.resize === 'vertical') {
|
||||
ta.style.resize = 'none';
|
||||
} else if (style.resize === 'both') {
|
||||
ta.style.resize = 'horizontal';
|
||||
}
|
||||
|
||||
if (style.boxSizing === 'content-box') {
|
||||
heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom));
|
||||
} else {
|
||||
heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
|
||||
}
|
||||
// Fix when a textarea is not on document body and heightOffset is Not a Number
|
||||
if (isNaN(heightOffset)) {
|
||||
heightOffset = 0;
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
function changeOverflow(value) {
|
||||
{
|
||||
// Chrome/Safari-specific fix:
|
||||
// When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space
|
||||
// made available by removing the scrollbar. The following forces the necessary text reflow.
|
||||
var width = ta.style.width;
|
||||
ta.style.width = '0px';
|
||||
// Force reflow:
|
||||
/* jshint ignore:start */
|
||||
ta.offsetWidth;
|
||||
/* jshint ignore:end */
|
||||
ta.style.width = width;
|
||||
}
|
||||
|
||||
overflowY = value;
|
||||
|
||||
if (setOverflowY) {
|
||||
ta.style.overflowY = value;
|
||||
}
|
||||
|
||||
resize();
|
||||
}
|
||||
|
||||
function resize() {
|
||||
var htmlTop = window.pageYOffset;
|
||||
var bodyTop = document.body.scrollTop;
|
||||
var originalHeight = ta.style.height;
|
||||
|
||||
ta.style.height = 'auto';
|
||||
|
||||
var endHeight = ta.scrollHeight + heightOffset;
|
||||
|
||||
if (ta.scrollHeight === 0) {
|
||||
// If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
|
||||
ta.style.height = originalHeight;
|
||||
return;
|
||||
}
|
||||
|
||||
ta.style.height = endHeight + 'px';
|
||||
|
||||
// used to check if an update is actually necessary on window.resize
|
||||
clientWidth = ta.clientWidth;
|
||||
|
||||
// prevents scroll-position jumping
|
||||
document.documentElement.scrollTop = htmlTop;
|
||||
document.body.scrollTop = bodyTop;
|
||||
}
|
||||
|
||||
function update() {
|
||||
var startHeight = ta.style.height;
|
||||
|
||||
resize();
|
||||
|
||||
var style = window.getComputedStyle(ta, null);
|
||||
|
||||
if (style.height !== ta.style.height) {
|
||||
if (overflowY !== 'visible') {
|
||||
changeOverflow('visible');
|
||||
}
|
||||
} else {
|
||||
if (overflowY !== 'hidden') {
|
||||
changeOverflow('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
if (startHeight !== ta.style.height) {
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('autosize:resized', true, false);
|
||||
ta.dispatchEvent(evt);
|
||||
}
|
||||
}
|
||||
|
||||
var pageResize = function pageResize() {
|
||||
if (ta.clientWidth !== clientWidth) {
|
||||
update();
|
||||
}
|
||||
};
|
||||
|
||||
var destroy = (function (style) {
|
||||
window.removeEventListener('resize', pageResize, false);
|
||||
ta.removeEventListener('input', update, false);
|
||||
ta.removeEventListener('keyup', update, false);
|
||||
ta.removeEventListener('autosize:destroy', destroy, false);
|
||||
ta.removeEventListener('autosize:update', update, false);
|
||||
set['delete'](ta);
|
||||
|
||||
Object.keys(style).forEach(function (key) {
|
||||
ta.style[key] = style[key];
|
||||
});
|
||||
}).bind(ta, {
|
||||
height: ta.style.height,
|
||||
resize: ta.style.resize,
|
||||
overflowY: ta.style.overflowY,
|
||||
overflowX: ta.style.overflowX,
|
||||
wordWrap: ta.style.wordWrap });
|
||||
|
||||
ta.addEventListener('autosize:destroy', destroy, false);
|
||||
|
||||
// IE9 does not fire onpropertychange or oninput for deletions,
|
||||
// so binding to onkeyup to catch most of those events.
|
||||
// There is no way that I know of to detect something like 'cut' in IE9.
|
||||
if ('onpropertychange' in ta && 'oninput' in ta) {
|
||||
ta.addEventListener('keyup', update, false);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', pageResize, false);
|
||||
ta.addEventListener('input', update, false);
|
||||
ta.addEventListener('autosize:update', update, false);
|
||||
set.add(ta);
|
||||
|
||||
if (setOverflowX) {
|
||||
ta.style.overflowX = 'hidden';
|
||||
ta.style.wordWrap = 'break-word';
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
function destroy(ta) {
|
||||
if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return;
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('autosize:destroy', true, false);
|
||||
ta.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
function update(ta) {
|
||||
if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return;
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('autosize:update', true, false);
|
||||
ta.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
var autosize = null;
|
||||
|
||||
// Do nothing in Node.js environment and IE8 (or lower)
|
||||
if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') {
|
||||
autosize = function (el) {
|
||||
return el;
|
||||
};
|
||||
autosize.destroy = function (el) {
|
||||
return el;
|
||||
};
|
||||
autosize.update = function (el) {
|
||||
return el;
|
||||
};
|
||||
} else {
|
||||
autosize = function (el, options) {
|
||||
if (el) {
|
||||
Array.prototype.forEach.call(el.length ? el : [el], function (x) {
|
||||
return assign(x, options);
|
||||
});
|
||||
}
|
||||
return el;
|
||||
};
|
||||
autosize.destroy = function (el) {
|
||||
if (el) {
|
||||
Array.prototype.forEach.call(el.length ? el : [el], destroy);
|
||||
}
|
||||
return el;
|
||||
};
|
||||
autosize.update = function (el) {
|
||||
if (el) {
|
||||
Array.prototype.forEach.call(el.length ? el : [el], update);
|
||||
}
|
||||
return el;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = autosize;
|
||||
});
|
2089
vendor/assets/javascripts/bootstrap-datepicker.js
vendored
2089
vendor/assets/javascripts/bootstrap-datepicker.js
vendored
File diff suppressed because it is too large
Load Diff
682
vendor/assets/javascripts/bootstrap-tagsinput.js
vendored
682
vendor/assets/javascripts/bootstrap-tagsinput.js
vendored
@ -1,682 +0,0 @@
|
||||
/*
|
||||
* bootstrap-tagsinput v0.8.0
|
||||
*
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
"use strict";
|
||||
|
||||
var defaultOptions = {
|
||||
tagClass: function(item) {
|
||||
return 'label label-info';
|
||||
},
|
||||
focusClass: 'focus',
|
||||
itemValue: function(item) {
|
||||
return item ? item.toString() : item;
|
||||
},
|
||||
itemText: function(item) {
|
||||
return this.itemValue(item);
|
||||
},
|
||||
itemTitle: function(item) {
|
||||
return null;
|
||||
},
|
||||
freeInput: true,
|
||||
addOnBlur: true,
|
||||
maxTags: undefined,
|
||||
maxChars: undefined,
|
||||
confirmKeys: [13, 44],
|
||||
delimiter: ',',
|
||||
delimiterRegex: null,
|
||||
cancelConfirmKeysOnEmpty: false,
|
||||
onTagExists: function(item, $tag) {
|
||||
$tag.hide().fadeIn();
|
||||
},
|
||||
trimValue: false,
|
||||
allowDuplicates: false,
|
||||
triggerChange: true
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor function
|
||||
*/
|
||||
function TagsInput(element, options) {
|
||||
this.isInit = true;
|
||||
this.itemsArray = [];
|
||||
|
||||
this.$element = $(element);
|
||||
this.$element.hide();
|
||||
|
||||
this.isSelect = (element.tagName === 'SELECT');
|
||||
this.multiple = (this.isSelect && element.hasAttribute('multiple'));
|
||||
this.objectItems = options && options.itemValue;
|
||||
this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : '';
|
||||
this.inputSize = Math.max(1, this.placeholderText.length);
|
||||
|
||||
this.$container = $('<div class="bootstrap-tagsinput"></div>');
|
||||
this.$input = $('<input type="text" placeholder="' + this.placeholderText + '"/>').appendTo(this.$container);
|
||||
|
||||
this.$element.before(this.$container);
|
||||
|
||||
this.build(options);
|
||||
this.isInit = false;
|
||||
}
|
||||
|
||||
TagsInput.prototype = {
|
||||
constructor: TagsInput,
|
||||
|
||||
/**
|
||||
* Adds the given item as a new tag. Pass true to dontPushVal to prevent
|
||||
* updating the elements val()
|
||||
*/
|
||||
add: function(item, dontPushVal, options) {
|
||||
var self = this;
|
||||
|
||||
if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags)
|
||||
return;
|
||||
|
||||
// Ignore falsey values, except false
|
||||
if (item !== false && !item)
|
||||
return;
|
||||
|
||||
// Trim value
|
||||
if (typeof item === "string" && self.options.trimValue) {
|
||||
item = $.trim(item);
|
||||
}
|
||||
|
||||
// Throw an error when trying to add an object while the itemValue option was not set
|
||||
if (typeof item === "object" && !self.objectItems)
|
||||
throw("Can't add objects when itemValue option is not set");
|
||||
|
||||
// Ignore strings only containg whitespace
|
||||
if (item.toString().match(/^\s*$/))
|
||||
return;
|
||||
|
||||
// If SELECT but not multiple, remove current tag
|
||||
if (self.isSelect && !self.multiple && self.itemsArray.length > 0)
|
||||
self.remove(self.itemsArray[0]);
|
||||
|
||||
if (typeof item === "string" && this.$element[0].tagName === 'INPUT') {
|
||||
var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter;
|
||||
var items = item.split(delimiter);
|
||||
if (items.length > 1) {
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
this.add(items[i], true);
|
||||
}
|
||||
|
||||
if (!dontPushVal)
|
||||
self.pushVal(self.options.triggerChange);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var itemValue = self.options.itemValue(item),
|
||||
itemText = self.options.itemText(item),
|
||||
tagClass = self.options.tagClass(item),
|
||||
itemTitle = self.options.itemTitle(item);
|
||||
|
||||
// Ignore items allready added
|
||||
var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0];
|
||||
if (existing && !self.options.allowDuplicates) {
|
||||
// Invoke onTagExists
|
||||
if (self.options.onTagExists) {
|
||||
var $existingTag = $(".tag", self.$container).filter(function() { return $(this).data("item") === existing; });
|
||||
self.options.onTagExists(item, $existingTag);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// if length greater than limit
|
||||
if (self.items().toString().length + item.length + 1 > self.options.maxInputLength)
|
||||
return;
|
||||
|
||||
// raise beforeItemAdd arg
|
||||
var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false, options: options});
|
||||
self.$element.trigger(beforeItemAddEvent);
|
||||
if (beforeItemAddEvent.cancel)
|
||||
return;
|
||||
|
||||
// register item in internal array and map
|
||||
self.itemsArray.push(item);
|
||||
|
||||
// add a tag element
|
||||
|
||||
var $tag = $('<span class="tag ' + htmlEncode(tagClass) + (itemTitle !== null ? ('" title="' + itemTitle) : '') + '">' + htmlEncode(itemText) + '<span data-role="remove"></span></span>');
|
||||
$tag.data('item', item);
|
||||
self.findInputWrapper().before($tag);
|
||||
$tag.after(' ');
|
||||
|
||||
// Check to see if the tag exists in its raw or uri-encoded form
|
||||
var optionExists = (
|
||||
$('option[value="' + encodeURIComponent(itemValue) + '"]', self.$element).length ||
|
||||
$('option[value="' + htmlEncode(itemValue) + '"]', self.$element).length
|
||||
);
|
||||
|
||||
// add <option /> if item represents a value not present in one of the <select />'s options
|
||||
if (self.isSelect && !optionExists) {
|
||||
var $option = $('<option selected>' + htmlEncode(itemText) + '</option>');
|
||||
$option.data('item', item);
|
||||
$option.attr('value', itemValue);
|
||||
self.$element.append($option);
|
||||
}
|
||||
|
||||
if (!dontPushVal)
|
||||
self.pushVal(self.options.triggerChange);
|
||||
|
||||
// Add class when reached maxTags
|
||||
if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength)
|
||||
self.$container.addClass('bootstrap-tagsinput-max');
|
||||
|
||||
// If using typeahead, once the tag has been added, clear the typeahead value so it does not stick around in the input.
|
||||
if ($('.typeahead, .twitter-typeahead', self.$container).length) {
|
||||
self.$input.typeahead('val', '');
|
||||
}
|
||||
|
||||
if (this.isInit) {
|
||||
self.$element.trigger($.Event('itemAddedOnInit', { item: item, options: options }));
|
||||
} else {
|
||||
self.$element.trigger($.Event('itemAdded', { item: item, options: options }));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the given item. Pass true to dontPushVal to prevent updating the
|
||||
* elements val()
|
||||
*/
|
||||
remove: function(item, dontPushVal, options) {
|
||||
var self = this;
|
||||
|
||||
if (self.objectItems) {
|
||||
if (typeof item === "object")
|
||||
item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == self.options.itemValue(item); } );
|
||||
else
|
||||
item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == item; } );
|
||||
|
||||
item = item[item.length-1];
|
||||
}
|
||||
|
||||
if (item) {
|
||||
var beforeItemRemoveEvent = $.Event('beforeItemRemove', { item: item, cancel: false, options: options });
|
||||
self.$element.trigger(beforeItemRemoveEvent);
|
||||
if (beforeItemRemoveEvent.cancel)
|
||||
return;
|
||||
|
||||
$('.tag', self.$container).filter(function() { return $(this).data('item') === item; }).remove();
|
||||
$('option', self.$element).filter(function() { return $(this).data('item') === item; }).remove();
|
||||
if($.inArray(item, self.itemsArray) !== -1)
|
||||
self.itemsArray.splice($.inArray(item, self.itemsArray), 1);
|
||||
}
|
||||
|
||||
if (!dontPushVal)
|
||||
self.pushVal(self.options.triggerChange);
|
||||
|
||||
// Remove class when reached maxTags
|
||||
if (self.options.maxTags > self.itemsArray.length)
|
||||
self.$container.removeClass('bootstrap-tagsinput-max');
|
||||
|
||||
self.$element.trigger($.Event('itemRemoved', { item: item, options: options }));
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes all items
|
||||
*/
|
||||
removeAll: function() {
|
||||
var self = this;
|
||||
|
||||
$('.tag', self.$container).remove();
|
||||
$('option', self.$element).remove();
|
||||
|
||||
while(self.itemsArray.length > 0)
|
||||
self.itemsArray.pop();
|
||||
|
||||
self.pushVal(self.options.triggerChange);
|
||||
},
|
||||
|
||||
/**
|
||||
* Refreshes the tags so they match the text/value of their corresponding
|
||||
* item.
|
||||
*/
|
||||
refresh: function() {
|
||||
var self = this;
|
||||
$('.tag', self.$container).each(function() {
|
||||
var $tag = $(this),
|
||||
item = $tag.data('item'),
|
||||
itemValue = self.options.itemValue(item),
|
||||
itemText = self.options.itemText(item),
|
||||
tagClass = self.options.tagClass(item);
|
||||
|
||||
// Update tag's class and inner text
|
||||
$tag.attr('class', null);
|
||||
$tag.addClass('tag ' + htmlEncode(tagClass));
|
||||
$tag.contents().filter(function() {
|
||||
return this.nodeType == 3;
|
||||
})[0].nodeValue = htmlEncode(itemText);
|
||||
|
||||
if (self.isSelect) {
|
||||
var option = $('option', self.$element).filter(function() { return $(this).data('item') === item; });
|
||||
option.attr('value', itemValue);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the items added as tags
|
||||
*/
|
||||
items: function() {
|
||||
return this.itemsArray;
|
||||
},
|
||||
|
||||
/**
|
||||
* Assembly value by retrieving the value of each item, and set it on the
|
||||
* element.
|
||||
*/
|
||||
pushVal: function() {
|
||||
var self = this,
|
||||
val = $.map(self.items(), function(item) {
|
||||
return self.options.itemValue(item).toString();
|
||||
});
|
||||
|
||||
self.$element.val(val, true);
|
||||
|
||||
if (self.options.triggerChange)
|
||||
self.$element.trigger('change');
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes the tags input behaviour on the element
|
||||
*/
|
||||
build: function(options) {
|
||||
var self = this;
|
||||
|
||||
self.options = $.extend({}, defaultOptions, options);
|
||||
// When itemValue is set, freeInput should always be false
|
||||
if (self.objectItems)
|
||||
self.options.freeInput = false;
|
||||
|
||||
makeOptionItemFunction(self.options, 'itemValue');
|
||||
makeOptionItemFunction(self.options, 'itemText');
|
||||
makeOptionFunction(self.options, 'tagClass');
|
||||
|
||||
// Typeahead Bootstrap version 2.3.2
|
||||
if (self.options.typeahead) {
|
||||
var typeahead = self.options.typeahead || {};
|
||||
|
||||
makeOptionFunction(typeahead, 'source');
|
||||
|
||||
self.$input.typeahead($.extend({}, typeahead, {
|
||||
source: function (query, process) {
|
||||
function processItems(items) {
|
||||
var texts = [];
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var text = self.options.itemText(items[i]);
|
||||
map[text] = items[i];
|
||||
texts.push(text);
|
||||
}
|
||||
process(texts);
|
||||
}
|
||||
|
||||
this.map = {};
|
||||
var map = this.map,
|
||||
data = typeahead.source(query);
|
||||
|
||||
if ($.isFunction(data.success)) {
|
||||
// support for Angular callbacks
|
||||
data.success(processItems);
|
||||
} else if ($.isFunction(data.then)) {
|
||||
// support for Angular promises
|
||||
data.then(processItems);
|
||||
} else {
|
||||
// support for functions and jquery promises
|
||||
$.when(data)
|
||||
.then(processItems);
|
||||
}
|
||||
},
|
||||
updater: function (text) {
|
||||
self.add(this.map[text]);
|
||||
return this.map[text];
|
||||
},
|
||||
matcher: function (text) {
|
||||
return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);
|
||||
},
|
||||
sorter: function (texts) {
|
||||
return texts.sort();
|
||||
},
|
||||
highlighter: function (text) {
|
||||
var regex = new RegExp( '(' + this.query + ')', 'gi' );
|
||||
return text.replace( regex, "<strong>$1</strong>" );
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// typeahead.js
|
||||
if (self.options.typeaheadjs) {
|
||||
var typeaheadConfig = null;
|
||||
var typeaheadDatasets = {};
|
||||
|
||||
// Determine if main configurations were passed or simply a dataset
|
||||
var typeaheadjs = self.options.typeaheadjs;
|
||||
if ($.isArray(typeaheadjs)) {
|
||||
typeaheadConfig = typeaheadjs[0];
|
||||
typeaheadDatasets = typeaheadjs[1];
|
||||
} else {
|
||||
typeaheadDatasets = typeaheadjs;
|
||||
}
|
||||
|
||||
self.$input.typeahead(typeaheadConfig, typeaheadDatasets).on('typeahead:selected', $.proxy(function (obj, datum) {
|
||||
if (typeaheadDatasets.valueKey)
|
||||
self.add(datum[typeaheadDatasets.valueKey]);
|
||||
else
|
||||
self.add(datum);
|
||||
self.$input.typeahead('val', '');
|
||||
}, self));
|
||||
}
|
||||
|
||||
self.$container.on('click', $.proxy(function(event) {
|
||||
if (! self.$element.attr('disabled')) {
|
||||
self.$input.removeAttr('disabled');
|
||||
}
|
||||
self.$input.focus();
|
||||
}, self));
|
||||
|
||||
if (self.options.addOnBlur && self.options.freeInput) {
|
||||
self.$input.on('focusout', $.proxy(function(event) {
|
||||
// HACK: only process on focusout when no typeahead opened, to
|
||||
// avoid adding the typeahead text as tag
|
||||
if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) {
|
||||
self.add(self.$input.val());
|
||||
self.$input.val('');
|
||||
}
|
||||
}, self));
|
||||
}
|
||||
|
||||
// Toggle the 'focus' css class on the container when it has focus
|
||||
self.$container.on({
|
||||
focusin: function() {
|
||||
self.$container.addClass(self.options.focusClass);
|
||||
},
|
||||
focusout: function() {
|
||||
self.$container.removeClass(self.options.focusClass);
|
||||
},
|
||||
});
|
||||
|
||||
self.$container.on('keydown', 'input', $.proxy(function(event) {
|
||||
var $input = $(event.target),
|
||||
$inputWrapper = self.findInputWrapper();
|
||||
|
||||
if (self.$element.attr('disabled')) {
|
||||
self.$input.attr('disabled', 'disabled');
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.which) {
|
||||
// BACKSPACE
|
||||
case 8:
|
||||
if (doGetCaretPosition($input[0]) === 0) {
|
||||
var prev = $inputWrapper.prev();
|
||||
if (prev.length) {
|
||||
self.remove(prev.data('item'));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// DELETE
|
||||
case 46:
|
||||
if (doGetCaretPosition($input[0]) === 0) {
|
||||
var next = $inputWrapper.next();
|
||||
if (next.length) {
|
||||
self.remove(next.data('item'));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// LEFT ARROW
|
||||
case 37:
|
||||
// Try to move the input before the previous tag
|
||||
var $prevTag = $inputWrapper.prev();
|
||||
if ($input.val().length === 0 && $prevTag[0]) {
|
||||
$prevTag.before($inputWrapper);
|
||||
$input.focus();
|
||||
}
|
||||
break;
|
||||
// RIGHT ARROW
|
||||
case 39:
|
||||
// Try to move the input after the next tag
|
||||
var $nextTag = $inputWrapper.next();
|
||||
if ($input.val().length === 0 && $nextTag[0]) {
|
||||
$nextTag.after($inputWrapper);
|
||||
$input.focus();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// ignore
|
||||
}
|
||||
|
||||
// Reset internal input's size
|
||||
var textLength = $input.val().length,
|
||||
wordSpace = Math.ceil(textLength / 5),
|
||||
size = textLength + wordSpace + 1;
|
||||
$input.attr('size', Math.max(this.inputSize, $input.val().length));
|
||||
}, self));
|
||||
|
||||
self.$container.on('keypress', 'input', $.proxy(function(event) {
|
||||
var $input = $(event.target);
|
||||
|
||||
if (self.$element.attr('disabled')) {
|
||||
self.$input.attr('disabled', 'disabled');
|
||||
return;
|
||||
}
|
||||
|
||||
var text = $input.val(),
|
||||
maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars;
|
||||
if (self.options.freeInput && (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached)) {
|
||||
// Only attempt to add a tag if there is data in the field
|
||||
if (text.length !== 0) {
|
||||
self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text);
|
||||
$input.val('');
|
||||
}
|
||||
|
||||
// If the field is empty, let the event triggered fire as usual
|
||||
if (self.options.cancelConfirmKeysOnEmpty === false) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
// Reset internal input's size
|
||||
var textLength = $input.val().length,
|
||||
wordSpace = Math.ceil(textLength / 5),
|
||||
size = textLength + wordSpace + 1;
|
||||
$input.attr('size', Math.max(this.inputSize, $input.val().length));
|
||||
}, self));
|
||||
|
||||
// Remove icon clicked
|
||||
self.$container.on('click', '[data-role=remove]', $.proxy(function(event) {
|
||||
if (self.$element.attr('disabled')) {
|
||||
return;
|
||||
}
|
||||
self.remove($(event.target).closest('.tag').data('item'));
|
||||
}, self));
|
||||
|
||||
// Only add existing value as tags when using strings as tags
|
||||
if (self.options.itemValue === defaultOptions.itemValue) {
|
||||
if (self.$element[0].tagName === 'INPUT') {
|
||||
self.add(self.$element.val());
|
||||
} else {
|
||||
$('option', self.$element).each(function() {
|
||||
self.add($(this).attr('value'), true);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes all tagsinput behaviour and unregsiter all event handlers
|
||||
*/
|
||||
destroy: function() {
|
||||
var self = this;
|
||||
|
||||
// Unbind events
|
||||
self.$container.off('keypress', 'input');
|
||||
self.$container.off('click', '[role=remove]');
|
||||
|
||||
self.$container.remove();
|
||||
self.$element.removeData('tagsinput');
|
||||
self.$element.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets focus on the tagsinput
|
||||
*/
|
||||
focus: function() {
|
||||
this.$input.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the internal input element
|
||||
*/
|
||||
input: function() {
|
||||
return this.$input;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the element which is wrapped around the internal input. This
|
||||
* is normally the $container, but typeahead.js moves the $input element.
|
||||
*/
|
||||
findInputWrapper: function() {
|
||||
var elt = this.$input[0],
|
||||
container = this.$container[0];
|
||||
while(elt && elt.parentNode !== container)
|
||||
elt = elt.parentNode;
|
||||
|
||||
return $(elt);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Register JQuery plugin
|
||||
*/
|
||||
$.fn.tagsinput = function(arg1, arg2, arg3) {
|
||||
var results = [];
|
||||
|
||||
this.each(function() {
|
||||
var tagsinput = $(this).data('tagsinput');
|
||||
// Initialize a new tags input
|
||||
if (!tagsinput) {
|
||||
tagsinput = new TagsInput(this, arg1);
|
||||
$(this).data('tagsinput', tagsinput);
|
||||
results.push(tagsinput);
|
||||
|
||||
if (this.tagName === 'SELECT') {
|
||||
$('option', $(this)).attr('selected', 'selected');
|
||||
}
|
||||
|
||||
// Init tags from $(this).val()
|
||||
$(this).val($(this).val());
|
||||
} else if (!arg1 && !arg2) {
|
||||
// tagsinput already exists
|
||||
// no function, trying to init
|
||||
results.push(tagsinput);
|
||||
} else if(tagsinput[arg1] !== undefined) {
|
||||
// Invoke function on existing tags input
|
||||
if(tagsinput[arg1].length === 3 && arg3 !== undefined){
|
||||
var retVal = tagsinput[arg1](arg2, null, arg3);
|
||||
}else{
|
||||
var retVal = tagsinput[arg1](arg2);
|
||||
}
|
||||
if (retVal !== undefined)
|
||||
results.push(retVal);
|
||||
}
|
||||
});
|
||||
|
||||
if ( typeof arg1 == 'string') {
|
||||
// Return the results from the invoked function calls
|
||||
return results.length > 1 ? results : results[0];
|
||||
} else {
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.tagsinput.Constructor = TagsInput;
|
||||
|
||||
/**
|
||||
* Most options support both a string or number as well as a function as
|
||||
* option value. This function makes sure that the option with the given
|
||||
* key in the given options is wrapped in a function
|
||||
*/
|
||||
function makeOptionItemFunction(options, key) {
|
||||
if (typeof options[key] !== 'function') {
|
||||
var propertyName = options[key];
|
||||
options[key] = function(item) { return item[propertyName]; };
|
||||
}
|
||||
}
|
||||
function makeOptionFunction(options, key) {
|
||||
if (typeof options[key] !== 'function') {
|
||||
var value = options[key];
|
||||
options[key] = function() { return value; };
|
||||
}
|
||||
}
|
||||
/**
|
||||
* HtmlEncodes the given value
|
||||
*/
|
||||
var htmlEncodeContainer = $('<div />');
|
||||
function htmlEncode(value) {
|
||||
if (value) {
|
||||
return htmlEncodeContainer.text(value).html();
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the caret in the given input field
|
||||
* http://flightschool.acylt.com/devnotes/caret-position-woes/
|
||||
*/
|
||||
function doGetCaretPosition(oField) {
|
||||
var iCaretPos = 0;
|
||||
if (document.selection) {
|
||||
oField.focus ();
|
||||
var oSel = document.selection.createRange();
|
||||
oSel.moveStart ('character', -oField.value.length);
|
||||
iCaretPos = oSel.text.length;
|
||||
} else if (oField.selectionStart || oField.selectionStart == '0') {
|
||||
iCaretPos = oField.selectionStart;
|
||||
}
|
||||
return (iCaretPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns boolean indicates whether user has pressed an expected key combination.
|
||||
* @param object keyPressEvent: JavaScript event object, refer
|
||||
* http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
|
||||
* @param object lookupList: expected key combinations, as in:
|
||||
* [13, {which: 188, shiftKey: true}]
|
||||
*/
|
||||
function keyCombinationInList(keyPressEvent, lookupList) {
|
||||
var found = false;
|
||||
$.each(lookupList, function (index, keyCombination) {
|
||||
if (typeof (keyCombination) === 'number' && keyPressEvent.which === keyCombination) {
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyPressEvent.which === keyCombination.which) {
|
||||
var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey,
|
||||
shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey,
|
||||
ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey;
|
||||
if (alt && shift && ctrl) {
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize tagsinput behaviour on inputs and selects which have
|
||||
* data-role=tagsinput
|
||||
*/
|
||||
$(function() {
|
||||
$("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput();
|
||||
});
|
||||
})(window.jQuery);
|
1257
vendor/assets/javascripts/chosen.jquery.js
vendored
1257
vendor/assets/javascripts/chosen.jquery.js
vendored
File diff suppressed because it is too large
Load Diff
9304
vendor/assets/javascripts/codemirror.js
vendored
9304
vendor/assets/javascripts/codemirror.js
vendored
File diff suppressed because it is too large
Load Diff
808
vendor/assets/javascripts/markdown.js
vendored
808
vendor/assets/javascripts/markdown.js
vendored
@ -1,808 +0,0 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
|
||||
|
||||
var htmlMode = CodeMirror.getMode(cmCfg, "text/html");
|
||||
var htmlModeMissing = htmlMode.name == "null"
|
||||
|
||||
function getMode(name) {
|
||||
if (CodeMirror.findModeByName) {
|
||||
var found = CodeMirror.findModeByName(name);
|
||||
if (found) name = found.mime || found.mimes[0];
|
||||
}
|
||||
var mode = CodeMirror.getMode(cmCfg, name);
|
||||
return mode.name == "null" ? null : mode;
|
||||
}
|
||||
|
||||
// Should characters that affect highlighting be highlighted separate?
|
||||
// Does not include characters that will be output (such as `1.` and `-` for lists)
|
||||
if (modeCfg.highlightFormatting === undefined)
|
||||
modeCfg.highlightFormatting = false;
|
||||
|
||||
// Maximum number of nested blockquotes. Set to 0 for infinite nesting.
|
||||
// Excess `>` will emit `error` token.
|
||||
if (modeCfg.maxBlockquoteDepth === undefined)
|
||||
modeCfg.maxBlockquoteDepth = 0;
|
||||
|
||||
// Should underscores in words open/close em/strong?
|
||||
if (modeCfg.underscoresBreakWords === undefined)
|
||||
modeCfg.underscoresBreakWords = true;
|
||||
|
||||
// Use `fencedCodeBlocks` to configure fenced code blocks. false to
|
||||
// disable, string to specify a precise regexp that the fence should
|
||||
// match, and true to allow three or more backticks or tildes (as
|
||||
// per CommonMark).
|
||||
|
||||
// Turn on task lists? ("- [ ] " and "- [x] ")
|
||||
if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
|
||||
|
||||
// Turn on strikethrough syntax
|
||||
if (modeCfg.strikethrough === undefined)
|
||||
modeCfg.strikethrough = false;
|
||||
|
||||
// Allow token types to be overridden by user-provided token types.
|
||||
if (modeCfg.tokenTypeOverrides === undefined)
|
||||
modeCfg.tokenTypeOverrides = {};
|
||||
|
||||
var tokenTypes = {
|
||||
header: "header",
|
||||
code: "comment",
|
||||
quote: "quote",
|
||||
list1: "variable-2",
|
||||
list2: "variable-3",
|
||||
list3: "keyword",
|
||||
hr: "hr",
|
||||
image: "image",
|
||||
imageAltText: "image-alt-text",
|
||||
imageMarker: "image-marker",
|
||||
formatting: "formatting",
|
||||
linkInline: "link",
|
||||
linkEmail: "link",
|
||||
linkText: "link",
|
||||
linkHref: "string",
|
||||
em: "em",
|
||||
strong: "strong",
|
||||
strikethrough: "strikethrough"
|
||||
};
|
||||
|
||||
for (var tokenType in tokenTypes) {
|
||||
if (tokenTypes.hasOwnProperty(tokenType) && modeCfg.tokenTypeOverrides[tokenType]) {
|
||||
tokenTypes[tokenType] = modeCfg.tokenTypeOverrides[tokenType];
|
||||
}
|
||||
}
|
||||
|
||||
var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
|
||||
, listRE = /^(?:[*\-+]|^[0-9]+([.)]))\s+/
|
||||
, taskListRE = /^\[(x| )\](?=\s)/ // Must follow listRE
|
||||
, atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
|
||||
, setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
|
||||
, textRE = /^[^#!\[\]*_\\<>` "'(~]+/
|
||||
, fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) +
|
||||
")[ \\t]*([\\w+#\-]*)");
|
||||
|
||||
function switchInline(stream, state, f) {
|
||||
state.f = state.inline = f;
|
||||
return f(stream, state);
|
||||
}
|
||||
|
||||
function switchBlock(stream, state, f) {
|
||||
state.f = state.block = f;
|
||||
return f(stream, state);
|
||||
}
|
||||
|
||||
function lineIsEmpty(line) {
|
||||
return !line || !/\S/.test(line.string)
|
||||
}
|
||||
|
||||
// Blocks
|
||||
|
||||
function blankLine(state) {
|
||||
// Reset linkTitle state
|
||||
state.linkTitle = false;
|
||||
// Reset EM state
|
||||
state.em = false;
|
||||
// Reset STRONG state
|
||||
state.strong = false;
|
||||
// Reset strikethrough state
|
||||
state.strikethrough = false;
|
||||
// Reset state.quote
|
||||
state.quote = 0;
|
||||
// Reset state.indentedCode
|
||||
state.indentedCode = false;
|
||||
if (state.f == htmlBlock) {
|
||||
state.f = inlineNormal;
|
||||
state.block = blockNormal;
|
||||
}
|
||||
// Reset state.trailingSpace
|
||||
state.trailingSpace = 0;
|
||||
state.trailingSpaceNewLine = false;
|
||||
// Mark this line as blank
|
||||
state.prevLine = state.thisLine
|
||||
state.thisLine = null
|
||||
return null;
|
||||
}
|
||||
|
||||
function blockNormal(stream, state) {
|
||||
|
||||
var sol = stream.sol();
|
||||
|
||||
var prevLineIsList = state.list !== false,
|
||||
prevLineIsIndentedCode = state.indentedCode;
|
||||
|
||||
state.indentedCode = false;
|
||||
|
||||
if (prevLineIsList) {
|
||||
if (state.indentationDiff >= 0) { // Continued list
|
||||
if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
|
||||
state.indentation -= state.indentationDiff;
|
||||
}
|
||||
state.list = null;
|
||||
} else if (state.indentation > 0) {
|
||||
state.list = null;
|
||||
} else { // No longer a list
|
||||
state.list = false;
|
||||
}
|
||||
}
|
||||
|
||||
var match = null;
|
||||
if (state.indentationDiff >= 4) {
|
||||
stream.skipToEnd();
|
||||
if (prevLineIsIndentedCode || lineIsEmpty(state.prevLine)) {
|
||||
state.indentation -= 4;
|
||||
state.indentedCode = true;
|
||||
return tokenTypes.code;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (stream.eatSpace()) {
|
||||
return null;
|
||||
} else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
|
||||
state.header = match[1].length;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "header";
|
||||
state.f = state.inline;
|
||||
return getType(state);
|
||||
} else if (!lineIsEmpty(state.prevLine) && !state.quote && !prevLineIsList &&
|
||||
!prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) {
|
||||
state.header = match[0].charAt(0) == '=' ? 1 : 2;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "header";
|
||||
state.f = state.inline;
|
||||
return getType(state);
|
||||
} else if (stream.eat('>')) {
|
||||
state.quote = sol ? 1 : state.quote + 1;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "quote";
|
||||
stream.eatSpace();
|
||||
return getType(state);
|
||||
} else if (stream.peek() === '[') {
|
||||
return switchInline(stream, state, footnoteLink);
|
||||
} else if (stream.match(hrRE, true)) {
|
||||
state.hr = true;
|
||||
return tokenTypes.hr;
|
||||
} else if (match = stream.match(listRE)) {
|
||||
var listType = match[1] ? "ol" : "ul";
|
||||
state.indentation = stream.column() + stream.current().length;
|
||||
state.list = true;
|
||||
|
||||
// While this list item's marker's indentation
|
||||
// is less than the deepest list item's content's indentation,
|
||||
// pop the deepest list item indentation off the stack.
|
||||
while (state.listStack && stream.column() < state.listStack[state.listStack.length - 1]) {
|
||||
state.listStack.pop();
|
||||
}
|
||||
|
||||
// Add this list item's content's indentation to the stack
|
||||
state.listStack.push(state.indentation);
|
||||
|
||||
if (modeCfg.taskLists && stream.match(taskListRE, false)) {
|
||||
state.taskList = true;
|
||||
}
|
||||
state.f = state.inline;
|
||||
if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
|
||||
return getType(state);
|
||||
} else if (modeCfg.fencedCodeBlocks && (match = stream.match(fencedCodeRE, true))) {
|
||||
state.fencedChars = match[1]
|
||||
// try switching mode
|
||||
state.localMode = getMode(match[2]);
|
||||
if (state.localMode) state.localState = CodeMirror.startState(state.localMode);
|
||||
state.f = state.block = local;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "code-block";
|
||||
state.code = -1
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
return switchInline(stream, state, state.inline);
|
||||
}
|
||||
|
||||
function htmlBlock(stream, state) {
|
||||
var style = htmlMode.token(stream, state.htmlState);
|
||||
if (!htmlModeMissing) {
|
||||
var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
|
||||
if ((inner.mode.name == "xml" && inner.state.tagStart === null &&
|
||||
(!inner.state.context && inner.state.tokenize.isInText)) ||
|
||||
(state.md_inside && stream.current().indexOf(">") > -1)) {
|
||||
state.f = inlineNormal;
|
||||
state.block = blockNormal;
|
||||
state.htmlState = null;
|
||||
}
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
function local(stream, state) {
|
||||
if (state.fencedChars && stream.match(state.fencedChars)) {
|
||||
if (modeCfg.highlightFormatting) state.formatting = "code-block";
|
||||
var returnType = getType(state)
|
||||
state.localMode = state.localState = null;
|
||||
state.block = blockNormal;
|
||||
state.f = inlineNormal;
|
||||
state.fencedChars = null;
|
||||
state.code = 0
|
||||
return returnType;
|
||||
} else if (state.fencedChars && stream.skipTo(state.fencedChars)) {
|
||||
return "comment"
|
||||
} else if (state.localMode) {
|
||||
return state.localMode.token(stream, state.localState);
|
||||
} else {
|
||||
stream.skipToEnd();
|
||||
return tokenTypes.code;
|
||||
}
|
||||
}
|
||||
|
||||
// Inline
|
||||
function getType(state) {
|
||||
var styles = [];
|
||||
|
||||
if (state.formatting) {
|
||||
styles.push(tokenTypes.formatting);
|
||||
|
||||
if (typeof state.formatting === "string") state.formatting = [state.formatting];
|
||||
|
||||
for (var i = 0; i < state.formatting.length; i++) {
|
||||
styles.push(tokenTypes.formatting + "-" + state.formatting[i]);
|
||||
|
||||
if (state.formatting[i] === "header") {
|
||||
styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.header);
|
||||
}
|
||||
|
||||
// Add `formatting-quote` and `formatting-quote-#` for blockquotes
|
||||
// Add `error` instead if the maximum blockquote nesting depth is passed
|
||||
if (state.formatting[i] === "quote") {
|
||||
if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
|
||||
styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.quote);
|
||||
} else {
|
||||
styles.push("error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.taskOpen) {
|
||||
styles.push("meta");
|
||||
return styles.length ? styles.join(' ') : null;
|
||||
}
|
||||
if (state.taskClosed) {
|
||||
styles.push("property");
|
||||
return styles.length ? styles.join(' ') : null;
|
||||
}
|
||||
|
||||
if (state.linkHref) {
|
||||
styles.push(tokenTypes.linkHref, "url");
|
||||
} else { // Only apply inline styles to non-url text
|
||||
if (state.strong) { styles.push(tokenTypes.strong); }
|
||||
if (state.em) { styles.push(tokenTypes.em); }
|
||||
if (state.strikethrough) { styles.push(tokenTypes.strikethrough); }
|
||||
if (state.linkText) { styles.push(tokenTypes.linkText); }
|
||||
if (state.code) { styles.push(tokenTypes.code); }
|
||||
if (state.image) { styles.push(tokenTypes.image); }
|
||||
if (state.imageAltText) { styles.push(tokenTypes.imageAltText, "link"); }
|
||||
if (state.imageMarker) { styles.push(tokenTypes.imageMarker); }
|
||||
}
|
||||
|
||||
if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + "-" + state.header); }
|
||||
|
||||
if (state.quote) {
|
||||
styles.push(tokenTypes.quote);
|
||||
|
||||
// Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
|
||||
if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
|
||||
styles.push(tokenTypes.quote + "-" + state.quote);
|
||||
} else {
|
||||
styles.push(tokenTypes.quote + "-" + modeCfg.maxBlockquoteDepth);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.list !== false) {
|
||||
var listMod = (state.listStack.length - 1) % 3;
|
||||
if (!listMod) {
|
||||
styles.push(tokenTypes.list1);
|
||||
} else if (listMod === 1) {
|
||||
styles.push(tokenTypes.list2);
|
||||
} else {
|
||||
styles.push(tokenTypes.list3);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.trailingSpaceNewLine) {
|
||||
styles.push("trailing-space-new-line");
|
||||
} else if (state.trailingSpace) {
|
||||
styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b"));
|
||||
}
|
||||
|
||||
return styles.length ? styles.join(' ') : null;
|
||||
}
|
||||
|
||||
function handleText(stream, state) {
|
||||
if (stream.match(textRE, true)) {
|
||||
return getType(state);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function inlineNormal(stream, state) {
|
||||
var style = state.text(stream, state);
|
||||
if (typeof style !== 'undefined')
|
||||
return style;
|
||||
|
||||
if (state.list) { // List marker (*, +, -, 1., etc)
|
||||
state.list = null;
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
if (state.taskList) {
|
||||
var taskOpen = stream.match(taskListRE, true)[1] !== "x";
|
||||
if (taskOpen) state.taskOpen = true;
|
||||
else state.taskClosed = true;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "task";
|
||||
state.taskList = false;
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
state.taskOpen = false;
|
||||
state.taskClosed = false;
|
||||
|
||||
if (state.header && stream.match(/^#+$/, true)) {
|
||||
if (modeCfg.highlightFormatting) state.formatting = "header";
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
// Get sol() value now, before character is consumed
|
||||
var sol = stream.sol();
|
||||
|
||||
var ch = stream.next();
|
||||
|
||||
// Matches link titles present on next line
|
||||
if (state.linkTitle) {
|
||||
state.linkTitle = false;
|
||||
var matchCh = ch;
|
||||
if (ch === '(') {
|
||||
matchCh = ')';
|
||||
}
|
||||
matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
|
||||
var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
|
||||
if (stream.match(new RegExp(regex), true)) {
|
||||
return tokenTypes.linkHref;
|
||||
}
|
||||
}
|
||||
|
||||
// If this block is changed, it may need to be updated in GFM mode
|
||||
if (ch === '`') {
|
||||
var previousFormatting = state.formatting;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "code";
|
||||
stream.eatWhile('`');
|
||||
var count = stream.current().length
|
||||
if (state.code == 0) {
|
||||
state.code = count
|
||||
return getType(state)
|
||||
} else if (count == state.code) { // Must be exact
|
||||
var t = getType(state)
|
||||
state.code = 0
|
||||
return t
|
||||
} else {
|
||||
state.formatting = previousFormatting
|
||||
return getType(state)
|
||||
}
|
||||
} else if (state.code) {
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
if (ch === '\\') {
|
||||
stream.next();
|
||||
if (modeCfg.highlightFormatting) {
|
||||
var type = getType(state);
|
||||
var formattingEscape = tokenTypes.formatting + "-escape";
|
||||
return type ? type + " " + formattingEscape : formattingEscape;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
|
||||
state.imageMarker = true;
|
||||
state.image = true;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "image";
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
if (ch === '[' && state.imageMarker && stream.match(/[^\]]*\](\(.*?\)| ?\[.*?\])/, false)) {
|
||||
state.imageMarker = false;
|
||||
state.imageAltText = true
|
||||
if (modeCfg.highlightFormatting) state.formatting = "image";
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
if (ch === ']' && state.imageAltText) {
|
||||
if (modeCfg.highlightFormatting) state.formatting = "image";
|
||||
var type = getType(state);
|
||||
state.imageAltText = false;
|
||||
state.image = false;
|
||||
state.inline = state.f = linkHref;
|
||||
return type;
|
||||
}
|
||||
|
||||
if (ch === '[' && stream.match(/[^\]]*\](\(.*\)| ?\[.*?\])/, false) && !state.image) {
|
||||
state.linkText = true;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
if (ch === ']' && state.linkText && stream.match(/\(.*?\)| ?\[.*?\]/, false)) {
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
var type = getType(state);
|
||||
state.linkText = false;
|
||||
state.inline = state.f = linkHref;
|
||||
return type;
|
||||
}
|
||||
|
||||
if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
|
||||
state.f = state.inline = linkInline;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
var type = getType(state);
|
||||
if (type){
|
||||
type += " ";
|
||||
} else {
|
||||
type = "";
|
||||
}
|
||||
return type + tokenTypes.linkInline;
|
||||
}
|
||||
|
||||
if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
|
||||
state.f = state.inline = linkInline;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
var type = getType(state);
|
||||
if (type){
|
||||
type += " ";
|
||||
} else {
|
||||
type = "";
|
||||
}
|
||||
return type + tokenTypes.linkEmail;
|
||||
}
|
||||
|
||||
if (ch === '<' && stream.match(/^(!--|[a-z]+(?:\s+[a-z_:.\-]+(?:\s*=\s*[^ >]+)?)*\s*>)/i, false)) {
|
||||
var end = stream.string.indexOf(">", stream.pos);
|
||||
if (end != -1) {
|
||||
var atts = stream.string.substring(stream.start, end);
|
||||
if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) state.md_inside = true;
|
||||
}
|
||||
stream.backUp(1);
|
||||
state.htmlState = CodeMirror.startState(htmlMode);
|
||||
return switchBlock(stream, state, htmlBlock);
|
||||
}
|
||||
|
||||
if (ch === '<' && stream.match(/^\/\w*?>/)) {
|
||||
state.md_inside = false;
|
||||
return "tag";
|
||||
}
|
||||
|
||||
var ignoreUnderscore = false;
|
||||
if (!modeCfg.underscoresBreakWords) {
|
||||
if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
|
||||
var prevPos = stream.pos - 2;
|
||||
if (prevPos >= 0) {
|
||||
var prevCh = stream.string.charAt(prevPos);
|
||||
if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
|
||||
ignoreUnderscore = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
|
||||
if (sol && stream.peek() === ' ') {
|
||||
// Do nothing, surrounded by newline and space
|
||||
} else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
|
||||
if (modeCfg.highlightFormatting) state.formatting = "strong";
|
||||
var t = getType(state);
|
||||
state.strong = false;
|
||||
return t;
|
||||
} else if (!state.strong && stream.eat(ch)) { // Add STRONG
|
||||
state.strong = ch;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "strong";
|
||||
return getType(state);
|
||||
} else if (state.em === ch) { // Remove EM
|
||||
if (modeCfg.highlightFormatting) state.formatting = "em";
|
||||
var t = getType(state);
|
||||
state.em = false;
|
||||
return t;
|
||||
} else if (!state.em) { // Add EM
|
||||
state.em = ch;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "em";
|
||||
return getType(state);
|
||||
}
|
||||
} else if (ch === ' ') {
|
||||
if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
|
||||
if (stream.peek() === ' ') { // Surrounded by spaces, ignore
|
||||
return getType(state);
|
||||
} else { // Not surrounded by spaces, back up pointer
|
||||
stream.backUp(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modeCfg.strikethrough) {
|
||||
if (ch === '~' && stream.eatWhile(ch)) {
|
||||
if (state.strikethrough) {// Remove strikethrough
|
||||
if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
|
||||
var t = getType(state);
|
||||
state.strikethrough = false;
|
||||
return t;
|
||||
} else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
|
||||
state.strikethrough = true;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
|
||||
return getType(state);
|
||||
}
|
||||
} else if (ch === ' ') {
|
||||
if (stream.match(/^~~/, true)) { // Probably surrounded by space
|
||||
if (stream.peek() === ' ') { // Surrounded by spaces, ignore
|
||||
return getType(state);
|
||||
} else { // Not surrounded by spaces, back up pointer
|
||||
stream.backUp(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ch === ' ') {
|
||||
if (stream.match(/ +$/, false)) {
|
||||
state.trailingSpace++;
|
||||
} else if (state.trailingSpace) {
|
||||
state.trailingSpaceNewLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
function linkInline(stream, state) {
|
||||
var ch = stream.next();
|
||||
|
||||
if (ch === ">") {
|
||||
state.f = state.inline = inlineNormal;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
var type = getType(state);
|
||||
if (type){
|
||||
type += " ";
|
||||
} else {
|
||||
type = "";
|
||||
}
|
||||
return type + tokenTypes.linkInline;
|
||||
}
|
||||
|
||||
stream.match(/^[^>]+/, true);
|
||||
|
||||
return tokenTypes.linkInline;
|
||||
}
|
||||
|
||||
function linkHref(stream, state) {
|
||||
// Check if space, and return NULL if so (to avoid marking the space)
|
||||
if(stream.eatSpace()){
|
||||
return null;
|
||||
}
|
||||
var ch = stream.next();
|
||||
if (ch === '(' || ch === '[') {
|
||||
state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]", 0);
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link-string";
|
||||
state.linkHref = true;
|
||||
return getType(state);
|
||||
}
|
||||
return 'error';
|
||||
}
|
||||
|
||||
var linkRE = {
|
||||
")": /^(?:[^\\\(\)]|\\.|\((?:[^\\\(\)]|\\.)*\))*?(?=\))/,
|
||||
"]": /^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\\]]|\\.)*\])*?(?=\])/
|
||||
}
|
||||
|
||||
function getLinkHrefInside(endChar) {
|
||||
return function(stream, state) {
|
||||
var ch = stream.next();
|
||||
|
||||
if (ch === endChar) {
|
||||
state.f = state.inline = inlineNormal;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link-string";
|
||||
var returnState = getType(state);
|
||||
state.linkHref = false;
|
||||
return returnState;
|
||||
}
|
||||
|
||||
stream.match(linkRE[endChar])
|
||||
state.linkHref = true;
|
||||
return getType(state);
|
||||
};
|
||||
}
|
||||
|
||||
function footnoteLink(stream, state) {
|
||||
if (stream.match(/^([^\]\\]|\\.)*\]:/, false)) {
|
||||
state.f = footnoteLinkInside;
|
||||
stream.next(); // Consume [
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
state.linkText = true;
|
||||
return getType(state);
|
||||
}
|
||||
return switchInline(stream, state, inlineNormal);
|
||||
}
|
||||
|
||||
function footnoteLinkInside(stream, state) {
|
||||
if (stream.match(/^\]:/, true)) {
|
||||
state.f = state.inline = footnoteUrl;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
var returnType = getType(state);
|
||||
state.linkText = false;
|
||||
return returnType;
|
||||
}
|
||||
|
||||
stream.match(/^([^\]\\]|\\.)+/, true);
|
||||
|
||||
return tokenTypes.linkText;
|
||||
}
|
||||
|
||||
function footnoteUrl(stream, state) {
|
||||
// Check if space, and return NULL if so (to avoid marking the space)
|
||||
if(stream.eatSpace()){
|
||||
return null;
|
||||
}
|
||||
// Match URL
|
||||
stream.match(/^[^\s]+/, true);
|
||||
// Check for link title
|
||||
if (stream.peek() === undefined) { // End of line, set flag to check next line
|
||||
state.linkTitle = true;
|
||||
} else { // More content on line, check if link title
|
||||
stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
|
||||
}
|
||||
state.f = state.inline = inlineNormal;
|
||||
return tokenTypes.linkHref + " url";
|
||||
}
|
||||
|
||||
var mode = {
|
||||
startState: function() {
|
||||
return {
|
||||
f: blockNormal,
|
||||
|
||||
prevLine: null,
|
||||
thisLine: null,
|
||||
|
||||
block: blockNormal,
|
||||
htmlState: null,
|
||||
indentation: 0,
|
||||
|
||||
inline: inlineNormal,
|
||||
text: handleText,
|
||||
|
||||
formatting: false,
|
||||
linkText: false,
|
||||
linkHref: false,
|
||||
linkTitle: false,
|
||||
code: 0,
|
||||
em: false,
|
||||
strong: false,
|
||||
header: 0,
|
||||
hr: false,
|
||||
taskList: false,
|
||||
list: false,
|
||||
listStack: [],
|
||||
quote: 0,
|
||||
trailingSpace: 0,
|
||||
trailingSpaceNewLine: false,
|
||||
strikethrough: false,
|
||||
fencedChars: null
|
||||
};
|
||||
},
|
||||
|
||||
copyState: function(s) {
|
||||
return {
|
||||
f: s.f,
|
||||
|
||||
prevLine: s.prevLine,
|
||||
thisLine: s.thisLine,
|
||||
|
||||
block: s.block,
|
||||
htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
|
||||
indentation: s.indentation,
|
||||
|
||||
localMode: s.localMode,
|
||||
localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
|
||||
|
||||
inline: s.inline,
|
||||
text: s.text,
|
||||
formatting: false,
|
||||
linkTitle: s.linkTitle,
|
||||
code: s.code,
|
||||
em: s.em,
|
||||
strong: s.strong,
|
||||
strikethrough: s.strikethrough,
|
||||
header: s.header,
|
||||
hr: s.hr,
|
||||
taskList: s.taskList,
|
||||
list: s.list,
|
||||
listStack: s.listStack.slice(0),
|
||||
quote: s.quote,
|
||||
indentedCode: s.indentedCode,
|
||||
trailingSpace: s.trailingSpace,
|
||||
trailingSpaceNewLine: s.trailingSpaceNewLine,
|
||||
md_inside: s.md_inside,
|
||||
fencedChars: s.fencedChars
|
||||
};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
|
||||
// Reset state.formatting
|
||||
state.formatting = false;
|
||||
|
||||
if (stream != state.thisLine) {
|
||||
var forceBlankLine = state.header || state.hr;
|
||||
|
||||
// Reset state.header and state.hr
|
||||
state.header = 0;
|
||||
state.hr = false;
|
||||
|
||||
if (stream.match(/^\s*$/, true) || forceBlankLine) {
|
||||
blankLine(state);
|
||||
if (!forceBlankLine) return null
|
||||
state.prevLine = null
|
||||
}
|
||||
|
||||
state.prevLine = state.thisLine
|
||||
state.thisLine = stream
|
||||
|
||||
// Reset state.taskList
|
||||
state.taskList = false;
|
||||
|
||||
// Reset state.trailingSpace
|
||||
state.trailingSpace = 0;
|
||||
state.trailingSpaceNewLine = false;
|
||||
|
||||
state.f = state.block;
|
||||
var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
|
||||
state.indentationDiff = Math.min(indentation - state.indentation, 4);
|
||||
state.indentation = state.indentation + state.indentationDiff;
|
||||
if (indentation > 0) return null;
|
||||
}
|
||||
return state.f(stream, state);
|
||||
},
|
||||
|
||||
innerMode: function(state) {
|
||||
if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
|
||||
if (state.localState) return {state: state.localState, mode: state.localMode};
|
||||
return {state: state, mode: mode};
|
||||
},
|
||||
|
||||
blankLine: blankLine,
|
||||
|
||||
getType: getType,
|
||||
|
||||
closeBrackets: "()[]{}''\"\"``",
|
||||
fold: "markdown"
|
||||
};
|
||||
return mode;
|
||||
}, "xml");
|
||||
|
||||
CodeMirror.defineMIME("text/x-markdown", "markdown");
|
||||
|
||||
});
|
2451
vendor/assets/javascripts/typeahead.js
vendored
2451
vendor/assets/javascripts/typeahead.js
vendored
File diff suppressed because it is too large
Load Diff
1548
vendor/assets/javascripts/underscore.js
vendored
1548
vendor/assets/javascripts/underscore.js
vendored
File diff suppressed because it is too large
Load Diff
0
vendor/assets/stylesheets/.keep
vendored
0
vendor/assets/stylesheets/.keep
vendored
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user