0.1.8rc1 (#56)
* bring latest pybambu module in from home assistant integration * add onBeforePrintStart callback to prevent starting "local" files and display print options dialog.* add onBeforePrintStart callback to prevent starting "local" files and display print options dialog (with AMS mapping support) * add AMS display in sidebar
This commit is contained in:
24
octoprint_bambu_printer/static/css/bambu_printer.css
Normal file
24
octoprint_bambu_printer/static/css/bambu_printer.css
Normal file
@ -0,0 +1,24 @@
|
||||
#sidebar_plugin_bambu_printer div.well {
|
||||
min-height: 70px;
|
||||
}
|
||||
|
||||
#sidebar_plugin_bambu_printer div.well div.span3.text-center div.row-fluid {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
#sidebar_plugin_bambu_printer div.well div.span3.text-center div.row-fluid.active {
|
||||
border: 2px solid;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#bambu_printer_print_options div.well {
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
#bambu_printer_print_options div.modal-body {
|
||||
overflow: inherit !important;
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,27 @@ $(function () {
|
||||
self.accessViewModel = parameters[3];
|
||||
self.timelapseViewModel = parameters[4];
|
||||
|
||||
self.use_ams = true;
|
||||
self.ams_mapping = ko.observableArray([]);
|
||||
|
||||
self.ams_mapping_computed = function(){
|
||||
var output_list = [];
|
||||
var index = 0;
|
||||
|
||||
ko.utils.arrayForEach(self.settingsViewModel.settings.plugins.bambu_printer.ams_data(), function(item){
|
||||
if(item){
|
||||
output_list = output_list.concat(item.tray());
|
||||
}
|
||||
});
|
||||
|
||||
ko.utils.arrayForEach(output_list, function(item){
|
||||
item["index"] = ko.observable(index);
|
||||
index++;
|
||||
});
|
||||
|
||||
return output_list;
|
||||
};
|
||||
|
||||
self.getAuthToken = function (data) {
|
||||
self.settingsViewModel.settings.plugins.bambu_printer.auth_token("");
|
||||
OctoPrint.simpleApiCommand("bambu_printer", "register", {
|
||||
@ -68,7 +89,6 @@ $(function () {
|
||||
}
|
||||
|
||||
if (data.files !== undefined) {
|
||||
console.log(data.files);
|
||||
self.listHelper.updateItems(data.files);
|
||||
self.listHelper.resetPage();
|
||||
}
|
||||
@ -78,71 +98,59 @@ $(function () {
|
||||
$('#bambu_timelapse').appendTo("#timelapse");
|
||||
};
|
||||
|
||||
self.onAfterBinding = function () {
|
||||
console.log(self.ams_mapping_computed());
|
||||
};
|
||||
|
||||
self.showTimelapseThumbnail = function(data) {
|
||||
$("#bambu_printer_timelapse_thumbnail").attr("src", data.thumbnail);
|
||||
$("#bambu_printer_timelapse_preview").modal('show');
|
||||
};
|
||||
|
||||
/*$('#files div.upload-buttons > span.fileinput-button:first, #files div.folder-button').remove();
|
||||
$('#files div.upload-buttons > span.fileinput-button:first').removeClass('span6').addClass('input-block-level');
|
||||
|
||||
self.onBeforePrintStart = function(start_print_command) {
|
||||
let confirmation_html = '' +
|
||||
' <div class="row-fluid form-vertical">\n' +
|
||||
' <div class="control-group">\n' +
|
||||
' <label class="control-label">' + gettext("Plate Number") + '</label>\n' +
|
||||
' <div class="controls">\n' +
|
||||
' <input type="number" min="1" value="1" id="bambu_printer_plate_number" class="input-mini">\n' +
|
||||
' </div>\n' +
|
||||
' </div>\n' +
|
||||
' </div>';
|
||||
|
||||
if(!self.settingsViewModel.settings.plugins.bambu_printer.always_use_default_options()){
|
||||
confirmation_html += '\n' +
|
||||
' <div class="row-fluid">\n' +
|
||||
' <div class="span6">\n' +
|
||||
' <label class="checkbox"><input id="bambu_printer_timelapse" type="checkbox"' + ((self.settingsViewModel.settings.plugins.bambu_printer.timelapse()) ? ' checked' : '') + '> ' + gettext("Enable timelapse") + '</label>\n' +
|
||||
' <label class="checkbox"><input id="bambu_printer_bed_leveling" type="checkbox"' + ((self.settingsViewModel.settings.plugins.bambu_printer.bed_leveling()) ? ' checked' : '') + '> ' + gettext("Enable bed leveling") + '</label>\n' +
|
||||
' <label class="checkbox"><input id="bambu_printer_flow_cali" type="checkbox"' + ((self.settingsViewModel.settings.plugins.bambu_printer.flow_cali()) ? ' checked' : '') + '> ' + gettext("Enable flow calibration") + '</label>\n' +
|
||||
' </div>\n' +
|
||||
' <div class="span6">\n' +
|
||||
' <label class="checkbox"><input id="bambu_printer_vibration_cali" type="checkbox"' + ((self.settingsViewModel.settings.plugins.bambu_printer.vibration_cali()) ? ' checked' : '') + '> ' + gettext("Enable vibration calibration") + '</label>\n' +
|
||||
' <label class="checkbox"><input id="bambu_printer_layer_inspect" type="checkbox"' + ((self.settingsViewModel.settings.plugins.bambu_printer.layer_inspect()) ? ' checked' : '') + '> ' + gettext("Enable first layer inspection") + '</label>\n' +
|
||||
' <label class="checkbox"><input id="bambu_printer_use_ams" type="checkbox"' + ((self.settingsViewModel.settings.plugins.bambu_printer.use_ams()) ? ' checked' : '') + '> ' + gettext("Use AMS") + '</label>\n' +
|
||||
' </div>\n' +
|
||||
' </div>\n';
|
||||
self.onBeforePrintStart = function(start_print_command, data) {
|
||||
self.ams_mapping(self.ams_mapping_computed());
|
||||
self.start_print_command = start_print_command;
|
||||
self.use_ams = self.settingsViewModel.settings.plugins.bambu_printer.use_ams();
|
||||
// prevent starting locally stored files, once data is added to core OctoPrint this
|
||||
// could be adjusted to include additional processing like get sliced file's
|
||||
// spool assignments and colors from plate_#.json inside 3mf file.
|
||||
if(data && data.origin !== "sdcard") {
|
||||
return false;
|
||||
}
|
||||
|
||||
showConfirmationDialog({
|
||||
title: "Bambu Print Options",
|
||||
html: confirmation_html,
|
||||
cancel: gettext("Cancel"),
|
||||
proceed: [gettext("Print"), gettext("Always")],
|
||||
onproceed: function (idx) {
|
||||
if(idx === 1){
|
||||
self.settingsViewModel.settings.plugins.bambu_printer.timelapse($('#bambu_printer_timelapse').is(':checked'));
|
||||
self.settingsViewModel.settings.plugins.bambu_printer.bed_leveling($('#bambu_printer_bed_leveling').is(':checked'));
|
||||
self.settingsViewModel.settings.plugins.bambu_printer.flow_cali($('#bambu_printer_flow_cali').is(':checked'));
|
||||
self.settingsViewModel.settings.plugins.bambu_printer.vibration_cali($('#bambu_printer_vibration_cali').is(':checked'));
|
||||
self.settingsViewModel.settings.plugins.bambu_printer.layer_inspect($('#bambu_printer_layer_inspect').is(':checked'));
|
||||
self.settingsViewModel.settings.plugins.bambu_printer.use_ams($('#bambu_printer_use_ams').is(':checked'));
|
||||
self.settingsViewModel.settings.plugins.bambu_printer.always_use_default_options(true);
|
||||
self.settingsViewModel.saveData();
|
||||
}
|
||||
// replace this with our own print command API call?
|
||||
start_print_command();
|
||||
},
|
||||
nofade: true
|
||||
});
|
||||
$("#bambu_printer_print_options").modal('show');
|
||||
return false;
|
||||
};*/
|
||||
};
|
||||
|
||||
self.toggle_spool_active = function(data) {
|
||||
if(data.index() >= 0){
|
||||
data.original_index = ko.observable(data.index());
|
||||
data.index(-1);
|
||||
} else {
|
||||
data.index(data.original_index());
|
||||
}
|
||||
};
|
||||
|
||||
self.cancel_print_options = function() {
|
||||
self.settingsViewModel.settings.plugins.bambu_printer.use_ams(self.use_ams);
|
||||
$("#bambu_printer_print_options").modal('hide');
|
||||
};
|
||||
|
||||
self.accept_print_options = function() {
|
||||
console.log("starting print!!!!");
|
||||
console.log(self.ams_mapping());
|
||||
$("#bambu_printer_print_options").modal('hide');
|
||||
var flattened_ams_mapping = ko.utils.arrayMap(self.ams_mapping(), function(item) {
|
||||
return item.index();
|
||||
});
|
||||
self.settingsViewModel.settings.plugins.bambu_printer.ams_mapping(flattened_ams_mapping);
|
||||
self.settingsViewModel.saveData(undefined, self.start_print_command);
|
||||
// self.settingsViewModel.saveData();
|
||||
};
|
||||
}
|
||||
|
||||
OCTOPRINT_VIEWMODELS.push({
|
||||
construct: Bambu_printerViewModel,
|
||||
// ViewModels your plugin depends on, e.g. loginStateViewModel, settingsViewModel, ...
|
||||
dependencies: ["settingsViewModel", "filesViewModel", "loginStateViewModel", "accessViewModel", "timelapseViewModel"],
|
||||
// Elements to bind to, e.g. #settings_plugin_bambu_printer, #tab_plugin_bambu_printer, ...
|
||||
elements: ["#bambu_printer_print_options", "#settings_plugin_bambu_printer", "#bambu_timelapse"]
|
||||
elements: ["#bambu_printer_print_options", "#settings_plugin_bambu_printer", "#bambu_timelapse", "#sidebar_plugin_bambu_printer"]
|
||||
});
|
||||
});
|
||||
|
8
octoprint_bambu_printer/static/js/jquery-ui.min.js
vendored
Normal file
8
octoprint_bambu_printer/static/js/jquery-ui.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
490
octoprint_bambu_printer/static/js/knockout-sortable.1.2.0.js
Normal file
490
octoprint_bambu_printer/static/js/knockout-sortable.1.2.0.js
Normal file
@ -0,0 +1,490 @@
|
||||
// knockout-sortable 1.2.0 | (c) 2019 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/ui/widgets/sortable", "jquery-ui/ui/widgets/draggable", "jquery-ui/ui/widgets/droppable"], factory);
|
||||
} else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
|
||||
// CommonJS module
|
||||
var ko = require("knockout"),
|
||||
jQuery = require("jquery");
|
||||
require("jquery-ui/ui/widgets/sortable");
|
||||
require("jquery-ui/ui/widgets/draggable");
|
||||
require("jquery-ui/ui/widgets/droppable");
|
||||
factory(ko, jQuery);
|
||||
} else {
|
||||
// No module loader (plain <script> tag) - put directly in global namespace
|
||||
factory(window.ko, window.jQuery);
|
||||
}
|
||||
})(function(ko, $) {
|
||||
var ITEMKEY = "ko_sortItem",
|
||||
INDEXKEY = "ko_sourceIndex",
|
||||
LISTKEY = "ko_sortList",
|
||||
PARENTKEY = "ko_parentList",
|
||||
DRAGKEY = "ko_dragItem",
|
||||
unwrap = ko.utils.unwrapObservable,
|
||||
dataGet = ko.utils.domData.get,
|
||||
dataSet = ko.utils.domData.set,
|
||||
version = $.ui && $.ui.version,
|
||||
//1.8.24 included a fix for how events were triggered in nested sortables. indexOf checks will fail if version starts with that value (0 vs. -1)
|
||||
hasNestedSortableFix = version && version.indexOf("1.6.") && version.indexOf("1.7.") && (version.indexOf("1.8.") || version === "1.8.24");
|
||||
|
||||
//internal afterRender that adds meta-data to children
|
||||
var addMetaDataAfterRender = function(elements, data) {
|
||||
ko.utils.arrayForEach(elements, function(element) {
|
||||
if (element.nodeType === 1) {
|
||||
dataSet(element, ITEMKEY, data);
|
||||
dataSet(element, PARENTKEY, dataGet(element.parentNode, LISTKEY));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//prepare the proper options for the template binding
|
||||
var prepareTemplateOptions = function(valueAccessor, dataName) {
|
||||
var result = {},
|
||||
options = {},
|
||||
actualAfterRender;
|
||||
|
||||
//build our options to pass to the template engine
|
||||
if (ko.utils.peekObservable(valueAccessor()).data) {
|
||||
options = unwrap(valueAccessor() || {});
|
||||
result[dataName] = options.data;
|
||||
if (options.hasOwnProperty("template")) {
|
||||
result.name = options.template;
|
||||
}
|
||||
} else {
|
||||
result[dataName] = valueAccessor();
|
||||
}
|
||||
|
||||
ko.utils.arrayForEach(["afterAdd", "afterRender", "as", "beforeRemove", "includeDestroyed", "templateEngine", "templateOptions", "nodes"], function (option) {
|
||||
if (options.hasOwnProperty(option)) {
|
||||
result[option] = options[option];
|
||||
} else if (ko.bindingHandlers.sortable.hasOwnProperty(option)) {
|
||||
result[option] = ko.bindingHandlers.sortable[option];
|
||||
}
|
||||
});
|
||||
|
||||
//use an afterRender function to add meta-data
|
||||
if (dataName === "foreach") {
|
||||
if (result.afterRender) {
|
||||
//wrap the existing function, if it was passed
|
||||
actualAfterRender = result.afterRender;
|
||||
result.afterRender = function(element, data) {
|
||||
addMetaDataAfterRender.call(data, element, data);
|
||||
actualAfterRender.call(data, element, data);
|
||||
};
|
||||
} else {
|
||||
result.afterRender = addMetaDataAfterRender;
|
||||
}
|
||||
}
|
||||
|
||||
//return options to pass to the template binding
|
||||
return result;
|
||||
};
|
||||
|
||||
var updateIndexFromDestroyedItems = function(index, items) {
|
||||
var unwrapped = unwrap(items);
|
||||
|
||||
if (unwrapped) {
|
||||
for (var i = 0; i <= index; i++) {
|
||||
//add one for every destroyed item we find before the targetIndex in the target array
|
||||
if (unwrapped[i] && unwrap(unwrapped[i]._destroy)) {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
};
|
||||
|
||||
//remove problematic leading/trailing whitespace from templates
|
||||
var stripTemplateWhitespace = function(element, name) {
|
||||
var templateSource,
|
||||
templateElement;
|
||||
|
||||
//process named templates
|
||||
if (name) {
|
||||
templateElement = document.getElementById(name);
|
||||
if (templateElement) {
|
||||
templateSource = new ko.templateSources.domElement(templateElement);
|
||||
templateSource.text($.trim(templateSource.text()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
//remove leading/trailing non-elements from anonymous templates
|
||||
$(element).contents().each(function() {
|
||||
if (this && this.nodeType !== 1) {
|
||||
element.removeChild(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//connect items with observableArrays
|
||||
ko.bindingHandlers.sortable = {
|
||||
init: function(element, valueAccessor, allBindingsAccessor, data, context) {
|
||||
var $element = $(element),
|
||||
value = unwrap(valueAccessor()) || {},
|
||||
templateOptions = prepareTemplateOptions(valueAccessor, "foreach"),
|
||||
sortable = {},
|
||||
startActual, updateActual;
|
||||
|
||||
stripTemplateWhitespace(element, templateOptions.name);
|
||||
|
||||
//build a new object that has the global options with overrides from the binding
|
||||
$.extend(true, sortable, ko.bindingHandlers.sortable);
|
||||
if (value.options && sortable.options) {
|
||||
ko.utils.extend(sortable.options, value.options);
|
||||
delete value.options;
|
||||
}
|
||||
ko.utils.extend(sortable, value);
|
||||
|
||||
//if allowDrop is an observable or a function, then execute it in a computed observable
|
||||
if (sortable.connectClass && (ko.isObservable(sortable.allowDrop) || typeof sortable.allowDrop == "function")) {
|
||||
ko.computed({
|
||||
read: function() {
|
||||
var value = unwrap(sortable.allowDrop),
|
||||
shouldAdd = typeof value == "function" ? value.call(this, templateOptions.foreach) : value;
|
||||
ko.utils.toggleDomNodeCssClass(element, sortable.connectClass, shouldAdd);
|
||||
},
|
||||
disposeWhenNodeIsRemoved: element
|
||||
}, this);
|
||||
} else {
|
||||
ko.utils.toggleDomNodeCssClass(element, sortable.connectClass, sortable.allowDrop);
|
||||
}
|
||||
|
||||
//wrap the template binding
|
||||
ko.bindingHandlers.template.init(element, function() { return templateOptions; }, allBindingsAccessor, data, context);
|
||||
|
||||
//keep a reference to start/update functions that might have been passed in
|
||||
startActual = sortable.options.start;
|
||||
updateActual = sortable.options.update;
|
||||
|
||||
//ensure draggable table row cells maintain their width while dragging (unless a helper is provided)
|
||||
if ( !sortable.options.helper ) {
|
||||
sortable.options.helper = function(e, ui) {
|
||||
if (ui.is("tr")) {
|
||||
ui.children().each(function() {
|
||||
$(this).width($(this).width());
|
||||
});
|
||||
}
|
||||
return ui;
|
||||
};
|
||||
}
|
||||
|
||||
//initialize sortable binding after template binding has rendered in update function
|
||||
var createTimeout = setTimeout(function() {
|
||||
var dragItem;
|
||||
var originalReceive = sortable.options.receive;
|
||||
|
||||
$element.sortable(ko.utils.extend(sortable.options, {
|
||||
start: function(event, ui) {
|
||||
//track original index
|
||||
var el = ui.item[0];
|
||||
dataSet(el, INDEXKEY, ko.utils.arrayIndexOf(ui.item.parent().children(), el));
|
||||
|
||||
//make sure that fields have a chance to update model
|
||||
ui.item.find("input:focus").change();
|
||||
if (startActual) {
|
||||
startActual.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
receive: function(event, ui) {
|
||||
//optionally apply an existing receive handler
|
||||
if (typeof originalReceive === "function") {
|
||||
originalReceive.call(this, event, ui);
|
||||
}
|
||||
|
||||
dragItem = dataGet(ui.item[0], DRAGKEY);
|
||||
if (dragItem) {
|
||||
//copy the model item, if a clone option is provided
|
||||
if (dragItem.clone) {
|
||||
dragItem = dragItem.clone();
|
||||
}
|
||||
|
||||
//configure a handler to potentially manipulate item before drop
|
||||
if (sortable.dragged) {
|
||||
dragItem = sortable.dragged.call(this, dragItem, event, ui) || dragItem;
|
||||
}
|
||||
}
|
||||
},
|
||||
update: function(event, ui) {
|
||||
var sourceParent, targetParent, sourceIndex, targetIndex, arg,
|
||||
el = ui.item[0],
|
||||
parentEl = ui.item.parent()[0],
|
||||
item = dataGet(el, ITEMKEY) || dragItem;
|
||||
|
||||
if (!item) {
|
||||
$(el).remove();
|
||||
}
|
||||
dragItem = null;
|
||||
|
||||
//make sure that moves only run once, as update fires on multiple containers
|
||||
if (item && (this === parentEl) || (!hasNestedSortableFix && $.contains(this, parentEl))) {
|
||||
//identify parents
|
||||
sourceParent = dataGet(el, PARENTKEY);
|
||||
sourceIndex = dataGet(el, INDEXKEY);
|
||||
targetParent = dataGet(el.parentNode, LISTKEY);
|
||||
targetIndex = ko.utils.arrayIndexOf(ui.item.parent().children(), el);
|
||||
|
||||
//take destroyed items into consideration
|
||||
if (!templateOptions.includeDestroyed) {
|
||||
sourceIndex = updateIndexFromDestroyedItems(sourceIndex, sourceParent);
|
||||
targetIndex = updateIndexFromDestroyedItems(targetIndex, targetParent);
|
||||
}
|
||||
|
||||
//build up args for the callbacks
|
||||
if (sortable.beforeMove || sortable.afterMove) {
|
||||
arg = {
|
||||
item: item,
|
||||
sourceParent: sourceParent,
|
||||
sourceParentNode: sourceParent && ui.sender || el.parentNode,
|
||||
sourceIndex: sourceIndex,
|
||||
targetParent: targetParent,
|
||||
targetIndex: targetIndex,
|
||||
cancelDrop: false
|
||||
};
|
||||
|
||||
//execute the configured callback prior to actually moving items
|
||||
if (sortable.beforeMove) {
|
||||
sortable.beforeMove.call(this, arg, event, ui);
|
||||
}
|
||||
}
|
||||
|
||||
//call cancel on the correct list, so KO can take care of DOM manipulation
|
||||
if (sourceParent) {
|
||||
$(sourceParent === targetParent ? this : ui.sender || this).sortable("cancel");
|
||||
}
|
||||
//for a draggable item just remove the element
|
||||
else {
|
||||
$(el).remove();
|
||||
}
|
||||
|
||||
//if beforeMove told us to cancel, then we are done
|
||||
if (arg && arg.cancelDrop) {
|
||||
return;
|
||||
}
|
||||
|
||||
//if the strategy option is unset or false, employ the order strategy involving removal and insertion of items
|
||||
if (!sortable.hasOwnProperty("strategyMove") || sortable.strategyMove === false) {
|
||||
//do the actual move
|
||||
if (targetIndex >= 0) {
|
||||
if (sourceParent) {
|
||||
sourceParent.splice(sourceIndex, 1);
|
||||
|
||||
//if using deferred updates plugin, force updates
|
||||
if (ko.processAllDeferredBindingUpdates) {
|
||||
ko.processAllDeferredBindingUpdates();
|
||||
}
|
||||
|
||||
//if using deferred updates on knockout 3.4, force updates
|
||||
if (ko.options && ko.options.deferUpdates) {
|
||||
ko.tasks.runEarly();
|
||||
}
|
||||
}
|
||||
|
||||
targetParent.splice(targetIndex, 0, item);
|
||||
}
|
||||
|
||||
//rendering is handled by manipulating the observableArray; ignore dropped element
|
||||
dataSet(el, ITEMKEY, null);
|
||||
}
|
||||
else { //employ the strategy of moving items
|
||||
if (targetIndex >= 0) {
|
||||
if (sourceParent) {
|
||||
if (sourceParent !== targetParent) {
|
||||
// moving from one list to another
|
||||
|
||||
sourceParent.splice(sourceIndex, 1);
|
||||
targetParent.splice(targetIndex, 0, item);
|
||||
|
||||
//rendering is handled by manipulating the observableArray; ignore dropped element
|
||||
dataSet(el, ITEMKEY, null);
|
||||
ui.item.remove();
|
||||
}
|
||||
else {
|
||||
// moving within same list
|
||||
var underlyingList = unwrap(sourceParent);
|
||||
|
||||
// notify 'beforeChange' subscribers
|
||||
if (sourceParent.valueWillMutate) {
|
||||
sourceParent.valueWillMutate();
|
||||
}
|
||||
|
||||
// move from source index ...
|
||||
underlyingList.splice(sourceIndex, 1);
|
||||
// ... to target index
|
||||
underlyingList.splice(targetIndex, 0, item);
|
||||
|
||||
// notify subscribers
|
||||
if (sourceParent.valueHasMutated) {
|
||||
sourceParent.valueHasMutated();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// drop new element from outside
|
||||
targetParent.splice(targetIndex, 0, item);
|
||||
|
||||
//rendering is handled by manipulating the observableArray; ignore dropped element
|
||||
dataSet(el, ITEMKEY, null);
|
||||
ui.item.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if using deferred updates plugin, force updates
|
||||
if (ko.processAllDeferredBindingUpdates) {
|
||||
ko.processAllDeferredBindingUpdates();
|
||||
}
|
||||
|
||||
//allow binding to accept a function to execute after moving the item
|
||||
if (sortable.afterMove) {
|
||||
sortable.afterMove.call(this, arg, event, ui);
|
||||
}
|
||||
}
|
||||
|
||||
if (updateActual) {
|
||||
updateActual.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
connectWith: sortable.connectClass ? "." + sortable.connectClass : false
|
||||
}));
|
||||
|
||||
//handle enabling/disabling sorting
|
||||
if (sortable.isEnabled !== undefined) {
|
||||
ko.computed({
|
||||
read: function() {
|
||||
$element.sortable(unwrap(sortable.isEnabled) ? "enable" : "disable");
|
||||
},
|
||||
disposeWhenNodeIsRemoved: element
|
||||
});
|
||||
}
|
||||
}, 0);
|
||||
|
||||
//handle disposal
|
||||
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
|
||||
//only call destroy if sortable has been created
|
||||
if ($element.data("ui-sortable") || $element.data("sortable")) {
|
||||
$element.sortable("destroy");
|
||||
}
|
||||
|
||||
ko.utils.toggleDomNodeCssClass(element, sortable.connectClass, false);
|
||||
|
||||
//do not create the sortable if the element has been removed from DOM
|
||||
clearTimeout(createTimeout);
|
||||
});
|
||||
|
||||
return { 'controlsDescendantBindings': true };
|
||||
},
|
||||
update: function(element, valueAccessor, allBindingsAccessor, data, context) {
|
||||
var templateOptions = prepareTemplateOptions(valueAccessor, "foreach");
|
||||
|
||||
//attach meta-data
|
||||
dataSet(element, LISTKEY, templateOptions.foreach);
|
||||
|
||||
//call template binding's update with correct options
|
||||
ko.bindingHandlers.template.update(element, function() { return templateOptions; }, allBindingsAccessor, data, context);
|
||||
},
|
||||
connectClass: 'ko_container',
|
||||
allowDrop: true,
|
||||
afterMove: null,
|
||||
beforeMove: null,
|
||||
options: {}
|
||||
};
|
||||
|
||||
//create a draggable that is appropriate for dropping into a sortable
|
||||
ko.bindingHandlers.draggable = {
|
||||
init: function(element, valueAccessor, allBindingsAccessor, data, context) {
|
||||
var value = unwrap(valueAccessor()) || {},
|
||||
options = value.options || {},
|
||||
draggableOptions = ko.utils.extend({}, ko.bindingHandlers.draggable.options),
|
||||
templateOptions = prepareTemplateOptions(valueAccessor, "data"),
|
||||
connectClass = value.connectClass || ko.bindingHandlers.draggable.connectClass,
|
||||
isEnabled = value.isEnabled !== undefined ? value.isEnabled : ko.bindingHandlers.draggable.isEnabled;
|
||||
|
||||
value = "data" in value ? value.data : value;
|
||||
|
||||
//set meta-data
|
||||
dataSet(element, DRAGKEY, value);
|
||||
|
||||
//override global options with override options passed in
|
||||
ko.utils.extend(draggableOptions, options);
|
||||
|
||||
//setup connection to a sortable
|
||||
draggableOptions.connectToSortable = connectClass ? "." + connectClass : false;
|
||||
|
||||
//initialize draggable
|
||||
$(element).draggable(draggableOptions);
|
||||
|
||||
//handle enabling/disabling sorting
|
||||
if (isEnabled !== undefined) {
|
||||
ko.computed({
|
||||
read: function() {
|
||||
$(element).draggable(unwrap(isEnabled) ? "enable" : "disable");
|
||||
},
|
||||
disposeWhenNodeIsRemoved: element
|
||||
});
|
||||
}
|
||||
|
||||
//handle disposal
|
||||
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
|
||||
$(element).draggable("destroy");
|
||||
});
|
||||
|
||||
return ko.bindingHandlers.template.init(element, function() { return templateOptions; }, allBindingsAccessor, data, context);
|
||||
},
|
||||
update: function(element, valueAccessor, allBindingsAccessor, data, context) {
|
||||
var templateOptions = prepareTemplateOptions(valueAccessor, "data");
|
||||
|
||||
return ko.bindingHandlers.template.update(element, function() { return templateOptions; }, allBindingsAccessor, data, context);
|
||||
},
|
||||
connectClass: ko.bindingHandlers.sortable.connectClass,
|
||||
options: {
|
||||
helper: "clone"
|
||||
}
|
||||
};
|
||||
|
||||
// Simple Droppable Implementation
|
||||
// binding that updates (function or observable)
|
||||
ko.bindingHandlers.droppable = {
|
||||
init: function(element, valueAccessor, allBindingsAccessor, data, context) {
|
||||
var value = unwrap(valueAccessor()) || {},
|
||||
options = value.options || {},
|
||||
droppableOptions = ko.utils.extend({}, ko.bindingHandlers.droppable.options),
|
||||
isEnabled = value.isEnabled !== undefined ? value.isEnabled : ko.bindingHandlers.droppable.isEnabled;
|
||||
|
||||
//override global options with override options passed in
|
||||
ko.utils.extend(droppableOptions, options);
|
||||
|
||||
//get reference to drop method
|
||||
value = "data" in value ? value.data : valueAccessor();
|
||||
|
||||
//set drop method
|
||||
droppableOptions.drop = function(event, ui) {
|
||||
var droppedItem = dataGet(ui.draggable[0], DRAGKEY) || dataGet(ui.draggable[0], ITEMKEY);
|
||||
value(droppedItem);
|
||||
};
|
||||
|
||||
//initialize droppable
|
||||
$(element).droppable(droppableOptions);
|
||||
|
||||
//handle enabling/disabling droppable
|
||||
if (isEnabled !== undefined) {
|
||||
ko.computed({
|
||||
read: function() {
|
||||
$(element).droppable(unwrap(isEnabled) ? "enable": "disable");
|
||||
},
|
||||
disposeWhenNodeIsRemoved: element
|
||||
});
|
||||
}
|
||||
|
||||
//handle disposal
|
||||
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
|
||||
$(element).droppable("destroy");
|
||||
});
|
||||
},
|
||||
options: {
|
||||
accept: "*"
|
||||
}
|
||||
};
|
||||
});
|
Reference in New Issue
Block a user