big refactoring, and more
New routes, new juice flow, the tags on posts
This commit is contained in:
parent
d04a68db01
commit
1805cb3e17
46 changed files with 1339 additions and 515 deletions
189
web/Scripts/knockout-jqAutocomplete.js
Normal file
189
web/Scripts/knockout-jqAutocomplete.js
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
// knockout-jqAutocomplete 0.4.3 | (c) 2015 Ryan Niemeyer | http://www.opensource.org/licenses/mit-license
|
||||
;(function(factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
// AMD anonymous module
|
||||
define(["knockout", "jquery", "jquery-ui/autocomplete"], factory);
|
||||
} else {
|
||||
// No module loader - put directly in global namespace
|
||||
factory(window.ko, jQuery);
|
||||
}
|
||||
})(function(ko, $) {
|
||||
var JqAuto = function() {
|
||||
var self = this,
|
||||
unwrap = ko.utils.unwrapObservable; //support older KO versions that did not have ko.unwrap
|
||||
|
||||
//binding's init function
|
||||
this.init = function(element, valueAccessor, allBindings, data, context) {
|
||||
var existingSelect, existingChange,
|
||||
options = unwrap(valueAccessor()),
|
||||
config = {},
|
||||
filter = typeof options.filter === "function" ? options.filter : self.defaultFilter;
|
||||
|
||||
//extend with global options
|
||||
ko.utils.extend(config, self.options);
|
||||
//override with options passed in binding
|
||||
ko.utils.extend(config, options.options);
|
||||
|
||||
//get source from a function (can be remote call)
|
||||
if (typeof options.source === "function" && !ko.isObservable(options.source)) {
|
||||
config.source = function(request, response) {
|
||||
//provide a wrapper to the normal response callback
|
||||
var callback = function(data) {
|
||||
self.processOptions(valueAccessor, null, data, request, response);
|
||||
};
|
||||
|
||||
//call the provided function for retrieving data
|
||||
options.source.call(context.$data, request.term, callback);
|
||||
};
|
||||
}
|
||||
else {
|
||||
//process local data
|
||||
config.source = self.processOptions.bind(self, valueAccessor, filter, options.source);
|
||||
}
|
||||
|
||||
//save any passed in select/change calls
|
||||
existingSelect = typeof config.select === "function" && config.select;
|
||||
existingChange = typeof config.change === "function" && config.change;
|
||||
|
||||
//handle updating the actual value
|
||||
config.select = function(event, ui) {
|
||||
if (ui.item && ui.item.actual) {
|
||||
options.value(ui.item.actual);
|
||||
|
||||
if (ko.isWriteableObservable(options.dataValue)) {
|
||||
options.dataValue(ui.item.data);
|
||||
}
|
||||
}
|
||||
|
||||
if (existingSelect) {
|
||||
existingSelect.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
//user made a change without selecting a value from the list
|
||||
config.change = function(event, ui) {
|
||||
if (!ui.item || !ui.item.actual) {
|
||||
options.value(event.target && event.target.value);
|
||||
|
||||
if (ko.isWriteableObservable(options.dataValue)) {
|
||||
options.dataValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (existingChange) {
|
||||
existingChange.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
//initialize the widget
|
||||
var widget = $(element).autocomplete(config).data("ui-autocomplete");
|
||||
|
||||
//render a template for the items
|
||||
if (options.template) {
|
||||
widget._renderItem = self.renderItem.bind(self, options.template, context);
|
||||
}
|
||||
|
||||
//destroy the widget if KO removes the element
|
||||
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
|
||||
if (widget && typeof widget.destroy === "function") {
|
||||
widget.destroy();
|
||||
widget = null;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//the binding's update function. keep value in sync with model
|
||||
this.update = function(element, valueAccessor) {
|
||||
var propNames, sources,
|
||||
options = unwrap(valueAccessor()),
|
||||
value = unwrap(options && options.value);
|
||||
|
||||
if (!value && value !== 0) {
|
||||
value = "";
|
||||
}
|
||||
|
||||
// find the appropriate value for the input
|
||||
sources = unwrap(options.source);
|
||||
propNames = self.getPropertyNames(valueAccessor);
|
||||
|
||||
// if there is local data, then try to determine the appropriate value for the input
|
||||
if ($.isArray(sources) && propNames.value) {
|
||||
value = ko.utils.arrayFirst(sources, function (opt) {
|
||||
return opt[propNames.value] == value;
|
||||
}
|
||||
) || value;
|
||||
}
|
||||
|
||||
if (propNames.input && value && typeof value === "object") {
|
||||
element.value = value[propNames.input];
|
||||
}
|
||||
else {
|
||||
element.value = value;
|
||||
}
|
||||
};
|
||||
|
||||
//if dealing with local data, the default filtering function
|
||||
this.defaultFilter = function(item, term) {
|
||||
term = term && term.toLowerCase();
|
||||
return (item || item === 0) && ko.toJSON(item).toLowerCase().indexOf(term) > -1;
|
||||
};
|
||||
|
||||
//filter/map options to be in a format that autocomplete requires
|
||||
this.processOptions = function(valueAccessor, filter, data, request, response) {
|
||||
var item, index, length,
|
||||
items = unwrap(data) || [],
|
||||
results = [],
|
||||
props = this.getPropertyNames(valueAccessor);
|
||||
|
||||
//filter/map items
|
||||
for (index = 0, length = items.length; index < length; index++) {
|
||||
item = items[index];
|
||||
|
||||
if (!filter || filter(item, request.term)) {
|
||||
results.push({
|
||||
label: props.label ? item[props.label] : item.toString(),
|
||||
value: props.input ? item[props.input] : item.toString(),
|
||||
actual: props.value ? item[props.value] : item,
|
||||
data: item
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//call autocomplete callback to display list
|
||||
response(results);
|
||||
};
|
||||
|
||||
//if specified, use a template to render an item
|
||||
this.renderItem = function(templateName, context, ul, item) {
|
||||
var $li = $("<li></li>").appendTo(ul),
|
||||
itemContext = context.createChildContext(item.data);
|
||||
|
||||
//apply the template binding
|
||||
ko.applyBindingsToNode($li[0], { template: templateName }, itemContext);
|
||||
|
||||
//clean up
|
||||
$li.one("remove", ko.cleanNode.bind(ko, $li[0]));
|
||||
|
||||
return $li;
|
||||
};
|
||||
|
||||
//retrieve the property names to use for the label, input, and value
|
||||
this.getPropertyNames = function(valueAccessor) {
|
||||
var options = ko.toJS(valueAccessor());
|
||||
|
||||
return {
|
||||
label: options.labelProp || options.valueProp,
|
||||
input: options.inputProp || options.labelProp || options.valueProp,
|
||||
value: options.valueProp
|
||||
};
|
||||
};
|
||||
|
||||
//default global options passed into autocomplete widget
|
||||
this.options = {
|
||||
autoFocus: true,
|
||||
delay: 50
|
||||
};
|
||||
};
|
||||
|
||||
ko.bindingHandlers.jqAuto = new JqAuto();
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue