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