|
|
@@ -16,249 +16,242 @@
|
|
|
|
|
|
// based on the original https://github.com/brianchance/knockout-x-editable
|
|
|
|
|
|
-(function (factory) {
|
|
|
- if (typeof define === "function" && define.amd) {
|
|
|
- // AMD anonymous module
|
|
|
- define(["knockout", "jquery"], factory);
|
|
|
- } else {
|
|
|
- // No module loader (plain <script> tag) - put directly in global namespace
|
|
|
- factory(window.ko, window.jQuery);
|
|
|
- }
|
|
|
-})(function (ko, $) {
|
|
|
- ko.bindingHandlers.editable = {
|
|
|
- init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
|
|
|
- var $element = $(element),
|
|
|
- value = valueAccessor(),
|
|
|
- allBindings = allBindingsAccessor(),
|
|
|
- editableOptions = allBindings.editableOptions || {};
|
|
|
+import $ from 'jquery';
|
|
|
+import ko from 'knockout';
|
|
|
|
|
|
- editableOptions.value = ko.utils.unwrapObservable(value);
|
|
|
+ko.bindingHandlers.editable = {
|
|
|
+ init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
|
|
|
+ var $element = $(element),
|
|
|
+ value = valueAccessor(),
|
|
|
+ allBindings = allBindingsAccessor(),
|
|
|
+ editableOptions = allBindings.editableOptions || {};
|
|
|
|
|
|
- if (!editableOptions.name) {
|
|
|
- $.each(bindingContext.$data, function (k, v) {
|
|
|
- if (v == value) {
|
|
|
- editableOptions.name = k;
|
|
|
- return false;
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
+ editableOptions.value = ko.utils.unwrapObservable(value);
|
|
|
|
|
|
- //wrap calls to knockout.validation
|
|
|
- if (!editableOptions.validate && value.isValid) {
|
|
|
- editableOptions.validate = function (testValue) {
|
|
|
- //have to set to new value, then call validate, then reset to original value
|
|
|
- //not pretty, but works
|
|
|
- var initalValue = value();
|
|
|
- value(testValue);
|
|
|
- var res = value.isValid() ? null : ko.utils.unwrapObservable(value.error);
|
|
|
- value(initalValue);
|
|
|
- return res;
|
|
|
+ if (!editableOptions.name) {
|
|
|
+ $.each(bindingContext.$data, function (k, v) {
|
|
|
+ if (v == value) {
|
|
|
+ editableOptions.name = k;
|
|
|
+ return false;
|
|
|
}
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ //wrap calls to knockout.validation
|
|
|
+ if (!editableOptions.validate && value.isValid) {
|
|
|
+ editableOptions.validate = function (testValue) {
|
|
|
+ //have to set to new value, then call validate, then reset to original value
|
|
|
+ //not pretty, but works
|
|
|
+ var initalValue = value();
|
|
|
+ value(testValue);
|
|
|
+ var res = value.isValid() ? null : ko.utils.unwrapObservable(value.error);
|
|
|
+ value(initalValue);
|
|
|
+ return res;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if ((editableOptions.type === 'select' || editableOptions.type === 'checklist' || editableOptions.type === 'typeahead') && !editableOptions.source && editableOptions.options) {
|
|
|
- if (editableOptions.optionsCaption)
|
|
|
- editableOptions.prepend = editableOptions.optionsCaption;
|
|
|
+ if ((editableOptions.type === 'select' || editableOptions.type === 'checklist' || editableOptions.type === 'typeahead') && !editableOptions.source && editableOptions.options) {
|
|
|
+ if (editableOptions.optionsCaption)
|
|
|
+ editableOptions.prepend = editableOptions.optionsCaption;
|
|
|
|
|
|
- //taken directly from ko.bindingHandlers['options']
|
|
|
- function applyToObject(object, predicate, defaultValue) {
|
|
|
- var predicateType = typeof predicate;
|
|
|
- if (predicateType === "function") // Given a function; run it against the data value
|
|
|
- return predicate(object);
|
|
|
- else if (predicateType === "string") // Given a string; treat it as a property name on the data value
|
|
|
- return object[predicate];
|
|
|
- else // Given no optionsText arg; use the data value itself
|
|
|
- return defaultValue;
|
|
|
- }
|
|
|
+ //taken directly from ko.bindingHandlers['options']
|
|
|
+ function applyToObject(object, predicate, defaultValue) {
|
|
|
+ var predicateType = typeof predicate;
|
|
|
+ if (predicateType === "function") // Given a function; run it against the data value
|
|
|
+ return predicate(object);
|
|
|
+ else if (predicateType === "string") // Given a string; treat it as a property name on the data value
|
|
|
+ return object[predicate];
|
|
|
+ else // Given no optionsText arg; use the data value itself
|
|
|
+ return defaultValue;
|
|
|
+ }
|
|
|
|
|
|
- editableOptions.source = function () {
|
|
|
- return ko.utils.arrayMap(editableOptions.options(), function (item) {
|
|
|
- var optionValue = applyToObject(item, editableOptions.optionsValue, item);
|
|
|
- var optionText = applyToObject(item, editableOptions.optionsText, optionText);
|
|
|
+ editableOptions.source = function () {
|
|
|
+ return ko.utils.arrayMap(editableOptions.options(), function (item) {
|
|
|
+ var optionValue = applyToObject(item, editableOptions.optionsValue, item);
|
|
|
+ var optionText = applyToObject(item, editableOptions.optionsText, optionText);
|
|
|
|
|
|
- return {
|
|
|
- value: ko.utils.unwrapObservable(optionValue),
|
|
|
- text: ko.utils.unwrapObservable(optionText)
|
|
|
- };
|
|
|
+ return {
|
|
|
+ value: ko.utils.unwrapObservable(optionValue),
|
|
|
+ text: ko.utils.unwrapObservable(optionText)
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (editableOptions.visible && ko.isObservable(editableOptions.visible)) {
|
|
|
+ editableOptions.toggle = 'manual';
|
|
|
+ }
|
|
|
+
|
|
|
+ var onActionRender = undefined;
|
|
|
+
|
|
|
+ if (editableOptions.inlineEditAction) {
|
|
|
+ onActionRender = function ($container, overflowing) {
|
|
|
+ if (!overflowing) {
|
|
|
+ var $editAction = $('<a href="javascript:void(0);"><i class="fa fa-fw fa-pencil"></i></a>');
|
|
|
+ if (editableOptions.inlineEditAction.editClass) {
|
|
|
+ $editAction.addClass(editableOptions.inlineEditAction.editClass);
|
|
|
+ }
|
|
|
+ $editAction.on('click', function () {
|
|
|
+ $editable.editable('toggle');
|
|
|
});
|
|
|
+ $editAction.appendTo($container);
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (editableOptions.visible && ko.isObservable(editableOptions.visible)) {
|
|
|
- editableOptions.toggle = 'manual';
|
|
|
+ var addPlaceHolder = function ($container) {
|
|
|
+ if (editableOptions.placeholder) {
|
|
|
+ $('<div>').addClass('editable-inline-empty').text(editableOptions.placeholder).click(function () {
|
|
|
+ $editable.editable('toggle');
|
|
|
+ }).appendTo($container);
|
|
|
}
|
|
|
+ };
|
|
|
|
|
|
- var onActionRender = undefined;
|
|
|
-
|
|
|
- if (editableOptions.inlineEditAction) {
|
|
|
- onActionRender = function ($container, overflowing) {
|
|
|
- if (!overflowing) {
|
|
|
- var $editAction = $('<a href="javascript:void(0);"><i class="fa fa-fw fa-pencil"></i></a>');
|
|
|
- if (editableOptions.inlineEditAction.editClass) {
|
|
|
- $editAction.addClass(editableOptions.inlineEditAction.editClass);
|
|
|
- }
|
|
|
- $editAction.on('click', function () {
|
|
|
- $editable.editable('toggle');
|
|
|
- });
|
|
|
- $editAction.appendTo($container);
|
|
|
+ var multiLineEllipsisHandler;
|
|
|
+ if (editableOptions.multiLineEllipsis) {
|
|
|
+ editableOptions.display = function (value) {
|
|
|
+ if (!value) {
|
|
|
+ if (multiLineEllipsisHandler) {
|
|
|
+ multiLineEllipsisHandler.pause();
|
|
|
}
|
|
|
+ var $container = $(this);
|
|
|
+ $container.empty();
|
|
|
+ addPlaceHolder($container);
|
|
|
+ if (onActionRender) {
|
|
|
+ onActionRender($container);
|
|
|
+ }
|
|
|
+ return;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- var addPlaceHolder = function ($container) {
|
|
|
- if (editableOptions.placeholder) {
|
|
|
- $('<div>').addClass('editable-inline-empty').text(editableOptions.placeholder).click(function () {
|
|
|
- $editable.editable('toggle');
|
|
|
- }).appendTo($container);
|
|
|
+ if (!multiLineEllipsisHandler) {
|
|
|
+ multiLineEllipsisHandler = new MultiLineEllipsisHandler({
|
|
|
+ element: this,
|
|
|
+ text: value,
|
|
|
+ overflowHeight: editableOptions.multiLineEllipsis.overflowHeight,
|
|
|
+ expandable: editableOptions.multiLineEllipsis,
|
|
|
+ expandActionClass: editableOptions.multiLineEllipsis.expandActionClass,
|
|
|
+ linkify: true,
|
|
|
+ onActionRender: onActionRender
|
|
|
+ });
|
|
|
+
|
|
|
+ ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
|
|
|
+ multiLineEllipsisHandler.dispose();
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ multiLineEllipsisHandler.setText(value);
|
|
|
+ multiLineEllipsisHandler.resume();
|
|
|
}
|
|
|
+ }
|
|
|
+ } else if (onActionRender) {
|
|
|
+ editableOptions.display = function (value) {
|
|
|
+ var $container = $(this);
|
|
|
+ if (!value) {
|
|
|
+ addPlaceHolder($container);
|
|
|
+ } else {
|
|
|
+ $('<span>').html(value).appendTo($container);
|
|
|
+ }
|
|
|
+ onActionRender($container);
|
|
|
};
|
|
|
|
|
|
- var multiLineEllipsisHandler;
|
|
|
- if (editableOptions.multiLineEllipsis) {
|
|
|
- editableOptions.display = function (value) {
|
|
|
- if (!value) {
|
|
|
- if (multiLineEllipsisHandler) {
|
|
|
- multiLineEllipsisHandler.pause();
|
|
|
- }
|
|
|
- var $container = $(this);
|
|
|
- $container.empty();
|
|
|
- addPlaceHolder($container);
|
|
|
- if (onActionRender) {
|
|
|
- onActionRender($container);
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
+ onActionRender = function ($container) {
|
|
|
+ var $editAction = $('<a href="javascript:void(0);"><i class="fa fa-fw fa-pencil"></i></a>');
|
|
|
+ if (editableOptions.inlineEditAction.editClass) {
|
|
|
+ $editAction.addClass(editableOptions.inlineEditAction.editClass);
|
|
|
+ }
|
|
|
+ $editAction.appendTo($container);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if (!multiLineEllipsisHandler) {
|
|
|
- multiLineEllipsisHandler = new MultiLineEllipsisHandler({
|
|
|
- element: this,
|
|
|
- text: value,
|
|
|
- overflowHeight: editableOptions.multiLineEllipsis.overflowHeight,
|
|
|
- expandable: editableOptions.multiLineEllipsis,
|
|
|
- expandActionClass: editableOptions.multiLineEllipsis.expandActionClass,
|
|
|
- linkify: true,
|
|
|
- onActionRender: onActionRender
|
|
|
- });
|
|
|
+ //create editable
|
|
|
+ var $editable = $element.editable(editableOptions);
|
|
|
|
|
|
- ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
|
|
|
- multiLineEllipsisHandler.dispose();
|
|
|
- });
|
|
|
- } else {
|
|
|
- multiLineEllipsisHandler.setText(value);
|
|
|
- multiLineEllipsisHandler.resume();
|
|
|
- }
|
|
|
+ if (editableOptions.multiLineEllipsis) {
|
|
|
+ $editable.off('.multiLine');
|
|
|
+ $editable.on('hidden.multiLine', function () {
|
|
|
+ if (multiLineEllipsisHandler && ko.unwrap(value)) {
|
|
|
+ multiLineEllipsisHandler.resume();
|
|
|
}
|
|
|
- } else if (onActionRender) {
|
|
|
- editableOptions.display = function (value) {
|
|
|
- var $container = $(this);
|
|
|
- if (!value) {
|
|
|
- addPlaceHolder($container);
|
|
|
+ });
|
|
|
+ $editable.on('shown.multiLine', function () {
|
|
|
+ if (multiLineEllipsisHandler) {
|
|
|
+ multiLineEllipsisHandler.pause();
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ //update observable on save
|
|
|
+ if (ko.isObservable(value)) {
|
|
|
+ $editable.on('save.ko', function (e, params) {
|
|
|
+ var newValue = params.newValue || '';
|
|
|
+ if (editableOptions.type === 'wysihtml5') {
|
|
|
+ if (editableOptions.skipNewLines) {
|
|
|
+ newValue = newValue.replace(/<br\s*[\/]?>/gi, ' ').replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
|
|
|
} else {
|
|
|
- $('<span>').html(value).appendTo($container);
|
|
|
+ newValue = newValue.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
|
|
|
}
|
|
|
- onActionRender($container);
|
|
|
- };
|
|
|
+ } else {
|
|
|
+ newValue = newValue.replace(/<(?:.|\n)*?>/gm, '');
|
|
|
|
|
|
- onActionRender = function ($container) {
|
|
|
- var $editAction = $('<a href="javascript:void(0);"><i class="fa fa-fw fa-pencil"></i></a>');
|
|
|
- if (editableOptions.inlineEditAction.editClass) {
|
|
|
- $editAction.addClass(editableOptions.inlineEditAction.editClass);
|
|
|
+ if (editableOptions.type !== 'textarea') {
|
|
|
+ newValue = newValue.replace(/\r?\n|\r/g, ' ');
|
|
|
}
|
|
|
- $editAction.appendTo($container);
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- //create editable
|
|
|
- var $editable = $element.editable(editableOptions);
|
|
|
+ value(newValue);
|
|
|
+ })
|
|
|
+ }
|
|
|
|
|
|
- if (editableOptions.multiLineEllipsis) {
|
|
|
- $editable.off('.multiLine');
|
|
|
- $editable.on('hidden.multiLine', function () {
|
|
|
- if (multiLineEllipsisHandler && ko.unwrap(value)) {
|
|
|
- multiLineEllipsisHandler.resume();
|
|
|
- }
|
|
|
+ if (editableOptions.toggleElement) {
|
|
|
+ var $clickable = $element.parent().find(editableOptions.toggleElement);
|
|
|
+ if ($element.parents('.show-inactive-on-hover').length > 0) {
|
|
|
+ $clickable = $element.parents('.show-inactive-on-hover').find(editableOptions.toggleElement);
|
|
|
+ }
|
|
|
+ if ($clickable !== null) {
|
|
|
+ $clickable.on('click', function (e) {
|
|
|
+ e.stopPropagation();
|
|
|
+ e.preventDefault();
|
|
|
+ $editable.editable('toggle');
|
|
|
});
|
|
|
- $editable.on('shown.multiLine', function () {
|
|
|
- if (multiLineEllipsisHandler) {
|
|
|
- multiLineEllipsisHandler.pause();
|
|
|
- }
|
|
|
- })
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- //update observable on save
|
|
|
- if (ko.isObservable(value)) {
|
|
|
- $editable.on('save.ko', function (e, params) {
|
|
|
- var newValue = params.newValue || '';
|
|
|
- if (editableOptions.type === 'wysihtml5') {
|
|
|
- if (editableOptions.skipNewLines) {
|
|
|
- newValue = newValue.replace(/<br\s*[\/]?>/gi, ' ').replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
|
|
|
- } else {
|
|
|
- newValue = newValue.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
|
|
|
- }
|
|
|
- } else {
|
|
|
- newValue = newValue.replace(/<(?:.|\n)*?>/gm, '');
|
|
|
-
|
|
|
- if (editableOptions.type !== 'textarea') {
|
|
|
- newValue = newValue.replace(/\r?\n|\r/g, ' ');
|
|
|
- }
|
|
|
- }
|
|
|
- value(newValue);
|
|
|
- })
|
|
|
- }
|
|
|
+ if (editableOptions.save) {
|
|
|
+ $editable.on('save', editableOptions.save.bind(viewModel));
|
|
|
+ }
|
|
|
|
|
|
- if (editableOptions.toggleElement) {
|
|
|
- var $clickable = $element.parent().find(editableOptions.toggleElement);
|
|
|
- if ($element.parents('.show-inactive-on-hover').length > 0) {
|
|
|
- $clickable = $element.parents('.show-inactive-on-hover').find(editableOptions.toggleElement);
|
|
|
+ //setup observable to fire only when editable changes, not when options change
|
|
|
+ //http://www.knockmeout.net/2012/06/knockoutjs-performance-gotcha-3-all-bindings.html
|
|
|
+ ko.computed({
|
|
|
+ read: function () {
|
|
|
+ var _allBindings = ko.utils.unwrapObservable(allBindingsAccessor());
|
|
|
+ var _options = _allBindings.editableOptions || {};
|
|
|
+ if (_options.enabled != null && _options.enabled) {
|
|
|
+ $editable.editable('enable');
|
|
|
}
|
|
|
- if ($clickable !== null) {
|
|
|
- $clickable.on('click', function (e) {
|
|
|
- e.stopPropagation();
|
|
|
- e.preventDefault();
|
|
|
- $editable.editable('toggle');
|
|
|
- });
|
|
|
+ else {
|
|
|
+ $editable.editable('disable');
|
|
|
}
|
|
|
- }
|
|
|
+ var val = ko.utils.unwrapObservable(valueAccessor());
|
|
|
+ if (val === null) val = '';
|
|
|
+ $editable.editable('setValue', val, true)
|
|
|
+ },
|
|
|
+ owner: this,
|
|
|
+ disposeWhenNodeIsRemoved: element
|
|
|
+ });
|
|
|
|
|
|
- if (editableOptions.save) {
|
|
|
- $editable.on('save', editableOptions.save.bind(viewModel));
|
|
|
- }
|
|
|
-
|
|
|
- //setup observable to fire only when editable changes, not when options change
|
|
|
- //http://www.knockmeout.net/2012/06/knockoutjs-performance-gotcha-3-all-bindings.html
|
|
|
+ if (editableOptions.visible && ko.isObservable(editableOptions.visible)) {
|
|
|
ko.computed({
|
|
|
read: function () {
|
|
|
- var _allBindings = ko.utils.unwrapObservable(allBindingsAccessor());
|
|
|
- var _options = _allBindings.editableOptions || {};
|
|
|
- if (_options.enabled != null && _options.enabled) {
|
|
|
- $editable.editable('enable');
|
|
|
- }
|
|
|
- else {
|
|
|
- $editable.editable('disable');
|
|
|
- }
|
|
|
- var val = ko.utils.unwrapObservable(valueAccessor());
|
|
|
- if (val === null) val = '';
|
|
|
- $editable.editable('setValue', val, true)
|
|
|
+ var val = ko.utils.unwrapObservable(editableOptions.visible());
|
|
|
+ if (val)
|
|
|
+ $editable.editable('show');
|
|
|
},
|
|
|
owner: this,
|
|
|
disposeWhenNodeIsRemoved: element
|
|
|
});
|
|
|
|
|
|
- if (editableOptions.visible && ko.isObservable(editableOptions.visible)) {
|
|
|
- ko.computed({
|
|
|
- read: function () {
|
|
|
- var val = ko.utils.unwrapObservable(editableOptions.visible());
|
|
|
- if (val)
|
|
|
- $editable.editable('show');
|
|
|
- },
|
|
|
- owner: this,
|
|
|
- disposeWhenNodeIsRemoved: element
|
|
|
- });
|
|
|
-
|
|
|
- $editable.on('hidden.ko', function (e, params) {
|
|
|
- editableOptions.visible(false);
|
|
|
- });
|
|
|
- }
|
|
|
+ $editable.on('hidden.ko', function (e, params) {
|
|
|
+ editableOptions.visible(false);
|
|
|
+ });
|
|
|
}
|
|
|
- };
|
|
|
-});
|
|
|
+ }
|
|
|
+};
|