Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save fomichigo/874489d748b2b9aa309c to your computer and use it in GitHub Desktop.
Save fomichigo/874489d748b2b9aa309c to your computer and use it in GitHub Desktop.
Binding handlers used in my App. Extracted from everywhere. Some of the original sources unknown.
ko.bindingHandlers.money = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor();
var allBindings = allBindingsAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value) || 0;
var output = output = accounting.formatMoney(valueUnwrapped, OKLO.CurrencySign);
if ($(element).is("input") === true) {
$(element).val(output);
} else {
$(element).html(output);
}
}
};
//<input type="text" data-bind="value: comment, valueUpdate: 'afterkeydown', maxLength: 20">
ko.bindingHandlers.maxLength = {
update: function (element, valueAccessor, allBindings) {
if (allBindings().value()) {
allBindings()
.value(allBindings().value().substr(0, valueAccessor()));
}
}
}
//<div data-bind="trimText: myText1"></div>
//<div data-bind="trimText: myText1, trimTextLength: 10"></div>
ko.bindingHandlers.trimLengthText = {};
ko.bindingHandlers.trimText = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var trimmedText = ko.computed(function () {
var untrimmedText = ko.utils.unwrapObservable(valueAccessor());
var defaultMaxLength = 20;
var minLength = 5;
var maxLength = ko.utils.unwrapObservable(allBindingsAccessor().trimTextLength) || defaultMaxLength;
if (maxLength < minLength) maxLength = minLength;
var text = untrimmedText.length > maxLength ? untrimmedText.substring(0, maxLength - 1) + '...' : untrimmedText;
return text;
});
ko.applyBindingsToNode(element, {
text: trimmedText
}, viewModel);
return {
controlsDescendantBindings: true
};
}
};
//<span data-bind="currency: price"></span>
//<span data-bind="currency: price, symbol: '€'"></span>
ko.bindingHandlers.currency = {
symbol: ko.observable('$'),
update: function (element, valueAccessor, allBindingsAccessor) {
return ko.bindingHandlers.text.update(element, function () {
var value = +(ko.utils.unwrapObservable(valueAccessor()) || 0),
symbol = ko.utils.unwrapObservable(allBindingsAccessor().symbol === undefined
? allBindingsAccessor().symbol
: ko.bindingHandlers.currency.symbol);
return symbol + value.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, "$1,");
});
}
};
//Usage: <span data-bind="text: FromDate, data: { 'item' : ItemID}"></span>
ko.bindingHandlers.data = {
name: ko.observable(''),
update: function (element, valueAccessor, allBindingsAccessor) {
var va = ko.utils.unwrapObservable(valueAccessor());
var value = "", name = "";
var d = {};
if (typeof (va) === "object") {
for (var k in va) {
var _val = ko.utils.unwrapObservable(va[k]);
if (typeof (_val) !== "object") {
d["data-" + k] = _val;
} else {
d["data-" + k] = ko.toJSON(_val);
}
}
} else {
value = va;
name = ko.utils.unwrapObservable(allBindingsAccessor().name !== undefined
? allBindingsAccessor().name
: ko.bindingHandlers.data.name);
d["data-" + name] = value;
}
ko.bindingHandlers.attr.update(element, function () {
return d;
});
}
};
// <a data-bind="href: myUrl">Click Me</a>
ko.bindingHandlers.href = {
update: function (element, valueAccessor) {
ko.bindingHandlers.attr.update(element, function () {
return { href: valueAccessor() };
});
}
};
ko.bindingHandlers.name = {
update: function (element, valueAccessor) {
ko.bindingHandlers.attr.update(element, function () {
return { name: valueAccessor() };
});
}
};
ko.bindingHandlers.id = {
update: function (element, valueAccessor) {
ko.bindingHandlers.attr.update(element, function () {
return { id: valueAccessor() };
});
}
};
// <img data-bind="src: imgSrc" />
ko.bindingHandlers.src = {
update: function (element, valueAccessor) {
ko.bindingHandlers.attr.update(element, function () {
return { src: valueAccessor() };
});
}
};
//<form data-bind="hidden: hideForm"></form>
ko.bindingHandlers.hidden = {
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
ko.bindingHandlers.visible.update(element, function () { return !value; });
}
};
/*
*
<!-- ko stopBinding: true -->
<div id="widget">
...
</div>
<!-- /ko -->
*
*/
ko.bindingHandlers.stopBinding = {
init: function () {
return { controlsDescendantBindings: true };
}
};
ko.virtualElements.allowedBindings.stopBinding = true;
//<pre data-bind="toJSON: $root"></pre>
ko.bindingHandlers.toJSON = {
update: function (element, valueAccessor) {
return ko.bindingHandlers.text.update(element, function () {
return OKLO.DebugEnabled ? ko.toJSON(valueAccessor(), null, 2) : "";
});
}
};
// Usage: <input tabindex="10" data-bind="numericValue: myNumericObservable">
// <input data-bind="numericValue: myNumericObservable, positions: 0, noDecimalPoints: true">
ko.bindingHandlers.numericValue = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
ko.utils.registerEventHandler(element, 'change', function (event) {
var observable = valueAccessor();
var positions = ko.utils.unwrapObservable(allBindingsAccessor().positions) || ko.bindingHandlers.numericValue.defaultPositions;
if (ko.utils.unwrapObservable(allBindingsAccessor().positions) == 0) {
positions = 0;
}
if (isNaN(parseFloat($(element).val())))
observable(0);
else {
if (!ko.utils.unwrapObservable(allBindingsAccessor().noDecimalPoints))
observable(parseFloat($(element).val().replace(".", "").replace(",", ".")).toFixed(positions).replace(",", "."));
else
observable(parseFloat($(element).val().replace(".", "").replace(",", ".")).toFixed(positions));
}
});
},
update: function (element, valueAccessor, allBindingsAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value != null) {
var positions = ko.utils.unwrapObservable(allBindingsAccessor().positions) || ko.bindingHandlers.numericValue.defaultPositions;
if (ko.utils.unwrapObservable(allBindingsAccessor().positions) == 0) {
positions = 0;
}
var formattedValue = parseFloat(value).toFixed(positions);
var finalFormatted = formattedValue;
if (!ko.utils.unwrapObservable(allBindingsAccessor().noDecimalPoints))
finalFormatted = ko.bindingHandlers.numericValue.withCommas(formattedValue);
ko.bindingHandlers.value.update(element, function () { return finalFormatted; });
}
},
defaultPositions: 2,
noDecimalPoints: false,
withCommas: function (original) {
original += '';
x = original.split('.');
x1 = x[0];
x2 = x.length > 1 ? ',' + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + '.' + '$2');
}
return x1 + x2;
}
};
ko.bindingHandlers.date = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor();
var allBindings = allBindingsAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);
// Date formats: http://momentjs.com/docs/#/displaying/format/
var pattern = allBindings.format || OKLO.DateFormat || "DD/MM/YYYY";
var output = "-";
if (valueUnwrapped !== null && valueUnwrapped !== undefined && valueUnwrapped.length > 0) {
if (valueUnwrapped && valueUnwrapped != "0001-01-01T00:00:00")
output = parseDate(valueUnwrapped).format(pattern);
}
if ($(element).is("input") === true) {
$(element).val(output);
} else {
$(element).text(output);
}
}
};
ko.bindingHandlers.datetime = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor();
var allBindings = allBindingsAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);
// Date formats: http://momentjs.com/docs/#/displaying/format/
var pattern = allBindings.format || OKLO.DateFormat || "DD/MM/YYYY";
var output = "-";
if (valueUnwrapped !== null && valueUnwrapped !== undefined && valueUnwrapped.length > 0) {
if (valueUnwrapped && valueUnwrapped != "0001-01-01T00:00:00")
output = moment(valueUnwrapped).format(pattern + " hh:mm:ss a");
}
if ($(element).is("input") === true) {
$(element).val(output);
} else {
$(element).text(output);
}
}
};
ko.bindingHandlers.duration = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor();
var allBindings = allBindingsAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);
var output = "-";
var format = allBindings.format || 'seconds';
if (valueUnwrapped !== null && valueUnwrapped !== undefined) {
output = moment.duration(valueUnwrapped, format).format("h [hrs] m [min]");
}
if ($(element).is("input") === true) {
$(element).val(output);
} else {
$(element).text(output);
}
}
};
ko.bindingHandlers.toggleClick = {
init: function (element, valueAccessor) {
var value = valueAccessor();
ko.utils.registerEventHandler(element, "click", function () {
if (value() != undefined)
value(!value());
else
value(false);
return false;
});
//ko.applyBindingsToNode(element, {
// click: function () {
// value(!value());
// }
//});
}
};
//<div data-bind="timeAgo: dateCreated"></div>
ko.bindingHandlers.timeAgo = {
update: function (element, valueAccessor) {
var val = unwrap(valueAccessor()),
date = new Date(val), // WARNING: this is not compatibile with IE8
timeAgo = toTimeAgo(date);
return ko.bindingHandlers.html.update(element, function () {
return '<time datetime="' + encodeURIComponent(val) + '">' + timeAgo + '</time>';
});
}
};
ko.bindingHandlers.loadingWhen = {
// any ViewModel using this extension needs a property called isLoading
// the div tag to contain the loaded content uses a data-bind="loadingWhen: isLoading"
init: function (element) {
var
$element = $(element),
currentPosition = $element.css("position");
$loader = $("<div>").addClass("loading-loader").hide();
//add the loader
$element.append($loader);
//console.log(currentPosition);
//make sure that we can absolutely position the loader against the original element
if (currentPosition == "auto" || currentPosition == "static")
$element.css("position", "relative");
//center the loader
$loader.css({
position: "absolute",
top: "50%",
left: "50%",
"margin-left": -($loader.width() / 2) + "px",
"margin-top": -($loader.height() / 2) + "px"
});
},
update: function (element, valueAccessor) {
var isLoading = ko.utils.unwrapObservable(valueAccessor()),
$element = $(element),
$childrenToHide = $element.children(":not(div.loading-loader)"),
$loader = $element.find("div.loading-loader");
if (isLoading) {
//$childrenToHide.css("visibility", "hidden").attr("disabled", "disabled");
$loader.show();
} else {
$loader.fadeOut("fast");
$childrenToHide.css("visibility", "visible").removeAttr("disabled");
}
}
};
ko.bindingHandlers.executeOnEnter = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var allBindings = allBindingsAccessor();
$(element).keydown(function (event) {
var keyCode = (event.which ? event.which : event.keyCode);
if (keyCode === 13 || keyCode === 9) {
allBindings.executeOnEnter.call(viewModel);
return false;
}
return true;
});
}
};
ko.bindingHandlers.dataTable = {
init: function (element, valueAccessor) {
var binding = ko.utils.unwrapObservable(valueAccessor());
// If the binding is an object with an options field,
// initialise the dataTable with those options.
if (binding.options) {
$(element).dataTable(binding.options);
}
},
update: function (element, valueAccessor) {
var binding = ko.utils.unwrapObservable(valueAccessor());
// If the binding isn't an object, turn it into one.
if (!binding.data) {
binding = { data: valueAccessor() }
}
// Clear table
$(element).dataTable().fnClearTable();
// Rebuild table from data source specified in binding
$(element).dataTable().fnAddData(binding.data());
}
};
ko.unapplyBindings = function ($node, remove) {
// unbind events
$node.find("*").each(function () {
$(this).unbind();
});
// Remove KO subscriptions and references
if (remove) {
ko.removeNode($node[0]);
} else {
ko.cleanNode($node[0]);
}
};
//wrapper for an observable that protects value until committed
ko.protectedObservable = function (initialValue) {
//private variables
var _temp = initialValue;
var _actual = ko.observable(initialValue);
var result = ko.dependentObservable({
read: _actual,
write: function (newValue) {
_temp = newValue;
}
});
//commit the temporary value to our observable, if it is different
result.commit = function () {
if (_temp !== _actual()) {
_actual(_temp);
}
};
//notify subscribers to update their value with the original
result.reset = function () {
_actual.valueHasMutated();
_temp = _actual();
};
return result;
};
ko.bindingHandlers.filePreview = {
update: function (element, valueAccessor, allBindingsAccessor) {
var allBindings = allBindingsAccessor();
if (!!FileReader && valueAccessor() && element.files.length) {
var reader = new FileReader();
reader.onload = function (event) {
var dataUri = event.target.result;
allBindings.imagePreviewUris.push(dataUri);
};
reader.onerror = function (e) {
console.log("error", e);
};
for (var i = 0; i < element.files.length; i++) {
reader.readAsDataURL(element.files[i]);
}
}
}
};
// http://jsfiddle.net/hd89y/
//<label class="btn btn-primary">
// <input type="radio" name="options" id="option1" value="1"
//data-bind="bsChecked: optionsValue"> Option 1
//</label>
ko.bindingHandlers.bsChecked = {
init: function (element, valueAccessor, allBindingsAccessor,
viewModel, bindingContext) {
var value = valueAccessor();
var newValueAccessor = function () {
return {
change: function () {
value(element.value);
}
}
};
ko.bindingHandlers.event.init(element, newValueAccessor,
allBindingsAccessor, viewModel, bindingContext);
},
update: function (element, valueAccessor, allBindingsAccessor,
viewModel, bindingContext) {
if ($(element).val() == ko.unwrap(valueAccessor())) {
$(element).closest('.btn').button('toggle');
}
}
};
ko.bindingHandlers.ckeditor = {
init: function (element, valueAccessor, allBindingsAccessor, context) {
// get observable
var modelValue = valueAccessor();;
var optionStandard = allBindingsAccessor().mode;
var options = {};
if (optionStandard) {
options = {
toolbarGroups: [
{ name: 'basicstyles', groups: ['basicstyles', 'cleanup'] },
{ name: 'paragraph', groups: ['list', 'indent', 'blocks', 'align', 'bidi'] },
{ name: 'links' },
{ name: 'styles' },
{ name: 'colors' }
]
};
}
$(element).ckeditor(function (textarea) {
// <span> element that contains the CKEditor markup
var $ckeContainer = $(this.container.$);
// <body> element within the iframe (<html> is contentEditable)
var $editorBody =
$ckeContainer.find('iframe').contents().find('body');
// sets the initial value
try {
$editorBody.html(modelValue());
} catch (e) {
}
// handle edits made in the editor - by typing
$editorBody.keyup(function () {
try {
modelValue($(this).html());
} catch (e) {
}
});
// handle edits made in the editor - by clicking in the toolbar
$ckeContainer.find('table.cke_editor').click(function () {
try {
modelValue($editorBody.html());
} catch (e) {
}
});
}, options);
// when ko disposes of <textarea>, destory the ckeditor instance
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).ckeditorGet().destroy(true);
});
},
update: function (element, valueAccessor, allBindingsAccessor, context) {
// handle programmatic updates to the observable
var newValue = ko.utils.unwrapObservable(valueAccessor());
var $ckeContainer = $(element).ckeditorGet().container;
if ($ckeContainer) {
// <span> element that contains the CKEditor markup
$ckeContainer = $($ckeContainer.$);
// <body> element within the iframe (<html> is contentEditable)
var $editorBody =
$ckeContainer.find('iframe').contents().find('body');
// if new value != existing value, replace it in the editor
if ($editorBody.html() != newValue)
$editorBody.html(newValue);
}
}
};
/*
<div data-bind="
value: selectedState,
select2: {
data: states,
placeholder: 'Select a State',
formatResult: format }"
class="select2" style="width: 200px"></div>
Selected: <span data-bind="text: selectedState"></span>
*/
ko.bindingHandlers.select2 = {
init: function (el, valueAccessor, allBindingsAccessor, viewModel) {
ko.utils.domNodeDisposal.addDisposeCallback(el, function () {
$(el).select2('destroy');
});
var allBindings = allBindingsAccessor(),
select2 = ko.utils.unwrapObservable(allBindings.select2);
$(el).select2('destroy');
$(el).select2(select2);
},
update: function (el, valueAccessor, allBindingsAccessor, viewModel) {
var allBindings = allBindingsAccessor();
if ("value" in allBindings) {
$(el).select2("val", allBindings.value());
} else if ("selectedOptions" in allBindings) {
var converted = [];
var textAccessor = function (value) { return value; };
if ("optionsText" in allBindings) {
textAccessor = function (value) {
var valueAccessor = function (item) { return item; }
if ("optionsValue" in allBindings) {
valueAccessor = function (item) { return item[allBindings.optionsValue]; }
}
var items = $.grep(allBindings.options(), function (e) { return valueAccessor(e) == value });
if (items.length == 0 || items.length > 1) {
return "UNKNOWN";
}
return items[0][allBindings.optionsText];
}
}
$.each(allBindings.selectedOptions(), function (key, value) {
converted.push({ id: value, text: textAccessor(value) });
});
$(el).select2("data", converted);
}
}
};
ko.bindingHandlers.foreachprop = {
transformObject: function (obj) {
var properties = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
properties.push({ key: key, value: obj[key] });
}
}
return properties;
},
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var value = ko.utils.unwrapObservable(valueAccessor()),
properties = ko.bindingHandlers.foreachprop.transformObject(value);
ko.applyBindingsToNode(element, { foreach: properties }, bindingContext);
return { controlsDescendantBindings: true };
}
};
/**
* Knockout Raty Binding
* Raty: http://www.wbotelhos.com/raty/
*
*/
(function ($, ko) {
var isObject = function (obj) { return (typeof obj === typeof {}); };
var isFunction = function (func) { return (Object.prototype.toString.call(func) === "[object Function]"); };
ko.bindingHandlers['raty'] = {
'init': function (element, valueAccessor) {
var options = {};
var value = valueAccessor();
value = isObject(value) ? value : {};
if ('rating' in value) {
var rating = value['rating'];
var click = !!value['click'] ? value['click'] : function (score, evt) { };
var start = !!value['start'] ? value['start'] : ko.utils.unwrapObservable(rating);
value['start'] = start;
value['click'] = !ko.isWriteableObservable(rating) ? click : function (score, evt) {
rating(score);
click.call(this, score, evt);
};
}
for (key in value) {
val = value[key];
options[key] = ko.utils.unwrapObservable(val);
};
options['path'] = OKLO.RootPath + 'Content/rating/images/';
$(element).raty(options);
},
'update': function (element, valueAccessor) {
var value = valueAccessor();
if ('rating' in value)
$(element).raty('click', ko.utils.unwrapObservable(value['rating']));
if ('readOnly' in value)
$(element).raty('readOnly', ko.utils.unwrapObservable(value['readOnly']));
}
};
})($, ko);
ko.bindingHandlers.imgLazy = {
init: function (element, valueAccessor) {
var options = valueAccessor() || {};
setTimeout(function () {
$(element).lazyload(options);
}, 0);
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).lazyload("destroy");
});
},
update: function (element, valueAccessor) {
var options = valueAccessor() || {};
if (options.updateOn && ko.isObservable(options.updateOn)) {
options.updateOn();
}
$(element).lazyload("destroy").lazyload(options);
}
};
ko.bindingHandlers.accordion = {
init: function (element, valueAccessor) {
var options = valueAccessor() || {};
setTimeout(function () {
$(element).smkAccordion(options);
}, 0);
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).smkAccordion("destroy");
});
},
update: function (element, valueAccessor) {
var options = valueAccessor() || {};
if (options.updateOn && ko.isObservable(options.updateOn)) {
options.updateOn();
}
$(element).smkAccordion("destroy").smkAccordion(options);
}
};
ko.bindingHandlers.templateWithOptions = {
init: ko.bindingHandlers.template.init,
update: function (element, valueAccessor, allBindingsAccessor, viewModel, context) {
var options = ko.utils.unwrapObservable(valueAccessor());
//if options were passed attach them to $data
if (options.templateOptions) {
context.$data.$item = ko.utils.unwrapObservable(options.templateOptions);
}
//call actual template binding
ko.bindingHandlers.template.update(element, valueAccessor, allBindingsAccessor, viewModel, context);
//clean up
delete context.$data.$item;
}
};
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var unwrap = ko.utils.unwrapObservable;
var dataSource = valueAccessor();
var binding = allBindingsAccessor();
var mParsedDate = parseDate(dataSource()).format(OKLO.DateFormat);
var options = {
keyboardNavigation: true,
todayHighlight: true,
autoclose: true,
format: OKLO.DateFormat.toLowerCase(),
startDate: binding.limit ? moment().format(OKLO.DateFormat) : null
};
if (binding.datePickerOptions) {
options = $.extend(options, binding.datePickerOptions);
}
$(element).datepicker(options);
$(element).datepicker('update', mParsedDate);
$(element).on("changeDate", function (ev) {
var observable = valueAccessor();
if ($(element).is(':focus')) {
// Don't update while the user is in the field...
// Instead, handle focus loss
$(element).on('blur', function (eb) {
var dateVal = $(element).datepicker("getDate");
observable(parseDate(dateVal).format(OKLO.DateFormat));
});
}
else {
observable(parseDate(ev.date).format(OKLO.DateFormat));
}
});
//handle removing an element from the dom
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker('remove');
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).datepicker('update', parseDate(value).format(OKLO.DateFormat));
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment