removed old view files

This commit is contained in:
Dan Elbert 2018-06-09 13:15:07 -05:00
parent 71a2ee4673
commit 306e6a85db
117 changed files with 1 additions and 30041 deletions

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

View File

@ -10,24 +10,6 @@
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives. // 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 . //= require_tree .
// Setup star rating automagic
$(document).on("turbolinks:load", function() {
$("input[data-rating='true']").starRating();
});

View File

@ -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);

View File

@ -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);

View File

@ -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("&times;"))
.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);
});
});

View File

@ -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);

View File

@ -1,7 +0,0 @@
(function($) {
$(document).on("turbolinks:load", function() {
$(".log-form input.datepicker").datepicker({autoclose: true, todayBtn: "linked", format: "yyyy-mm-dd"});
});
})(jQuery);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -10,97 +10,4 @@
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
* file per style scope. * 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;
}

View File

@ -1,5 +0,0 @@
.CodeMirror {
border: 1px solid $gray-light;
font-family: inconsolata monospace;
font-size: 15px;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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/

View File

@ -1,5 +0,0 @@
$brand-primary: darken(#93C54B, 10%);
$brand-success: $brand-primary;
$text-color: #3E3F3A;

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -13,57 +13,4 @@ module ApplicationHelper
decorated decorated
end 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 end

View File

@ -1,11 +1,4 @@
module IngredientsHelper module IngredientsHelper
def ndbn_button_class(ingredient)
if ingredient.ndbn.present?
'btn btn-success'
else
'btn btn-default'
end
end
end end

View File

@ -1,71 +1,3 @@
module RecipesHelper 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 end

View File

@ -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>

View File

@ -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>

View File

@ -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 %>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -1,3 +0,0 @@
$("#ingredient_form").replaceWith($("<%= escape_javascript(render(partial: 'ingredients/form')) %>"));
window.INGREDIENT_API.initialize();

View File

@ -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>

View File

@ -1,3 +0,0 @@
<li>
<%= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote %>
</li>

View File

@ -1,3 +0,0 @@
<li class='disabled'>
<%= content_tag :a, raw(t 'views.pagination.truncate') %>
</li>

View File

@ -1,3 +0,0 @@
<li>
<%= link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote} %>
</li>

View File

@ -1,3 +0,0 @@
<li>
<%= link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, :rel => 'next', :remote => remote %>
</li>

View File

@ -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 %>

View File

@ -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 -%>

View File

@ -1,3 +0,0 @@
<li>
<%= link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, :rel => 'prev', :remote => remote %>
</li>

View File

@ -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>

View File

@ -9,40 +9,11 @@
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', '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 %> <%= csrf_meta_tags %>
</head> </head>
<body> <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"> <div class="container" id="main_container">
@ -50,18 +21,6 @@
</div><!-- /.container --> </div><!-- /.container -->
<!--<footer class="footer">-->
<!--<div class="container-fluid">-->
<!--<div class="row">-->
<!--<div class="col-xs-12">-->
<!--<p class="text-muted text-right">&copy; Dan Elbert 2016</p>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</footer>-->
<%= yield(:page_bottom) %>
</body> </body>
</html> </html>

View File

@ -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 %>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 %>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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">&times;</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>

View File

@ -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 %>

View File

@ -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>

View File

@ -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">&times;</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>

View File

@ -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">&times;</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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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">&times;</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>

View File

@ -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 %>

View File

@ -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 %>

View File

@ -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>

View File

@ -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>

View File

@ -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>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 738 B

View File

View File

@ -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;
});

File diff suppressed because it is too large Load Diff

View File

@ -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);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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");
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

Some files were not shown because too many files have changed in this diff Show More