UNPKG

jquery.fancytree

Version:

JavaScript tree view / tree grid plugin with support for keyboard, inline editing, filtering, checkboxes, drag'n'drop, and lazy loading

342 lines (309 loc) 10.2 kB
/*! * jquery.fancytree.edit.js * * Make node titles editable. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/) * * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de) * * Released under the MIT license * https://github.com/mar10/fancytree/wiki/LicenseInfo * * @version 2.30.0 * @date 2018-09-02T15:42:49Z */ ;(function( factory ) { if ( typeof define === "function" && define.amd ) { // AMD. Register as an anonymous module. define( [ "jquery", "./jquery.fancytree" ], factory ); } else if ( typeof module === "object" && module.exports ) { // Node/CommonJS require("./jquery.fancytree"); module.exports = factory(require("jquery")); } else { // Browser globals factory( jQuery ); } }( function( $ ) { "use strict"; /******************************************************************************* * Private functions and variables */ var isMac = /Mac/.test(navigator.platform), escapeHtml = $.ui.fancytree.escapeHtml, unescapeHtml = $.ui.fancytree.unescapeHtml; /** * [ext-edit] Start inline editing of current node title. * * @alias FancytreeNode#editStart * @requires Fancytree */ $.ui.fancytree._FancytreeNodeClass.prototype.editStart = function(){ var $input, node = this, tree = this.tree, local = tree.ext.edit, instOpts = tree.options.edit, $title = $(".fancytree-title", node.span), eventData = { node: node, tree: tree, options: tree.options, isNew: $(node[tree.statusClassPropName]).hasClass("fancytree-edit-new"), orgTitle: node.title, input: null, dirty: false }; // beforeEdit may want to modify the title before editing if( instOpts.beforeEdit.call(node, {type: "beforeEdit"}, eventData) === false ) { return false; } $.ui.fancytree.assert(!local.currentNode, "recursive edit"); local.currentNode = this; local.eventData = eventData; // Disable standard Fancytree mouse- and key handling tree.widget._unbind(); // #116: ext-dnd prevents the blur event, so we have to catch outer clicks $(document).on("mousedown.fancytree-edit", function(event){ if( ! $(event.target).hasClass("fancytree-edit-input") ){ node.editEnd(true, event); } }); // Replace node with <input> $input = $("<input />", { "class": "fancytree-edit-input", type: "text", value: tree.options.escapeTitles ? eventData.orgTitle : unescapeHtml(eventData.orgTitle) }); local.eventData.input = $input; if ( instOpts.adjustWidthOfs != null ) { $input.width($title.width() + instOpts.adjustWidthOfs); } if ( instOpts.inputCss != null ) { $input.css(instOpts.inputCss); } $title.html($input); // Focus <input> and bind keyboard handler $input .focus() .change(function(event){ $input.addClass("fancytree-edit-dirty"); }).keydown(function(event){ switch( event.which ) { case $.ui.keyCode.ESCAPE: node.editEnd(false, event); break; case $.ui.keyCode.ENTER: node.editEnd(true, event); return false; // so we don't start editmode on Mac } event.stopPropagation(); }).blur(function(event){ return node.editEnd(true, event); }); instOpts.edit.call(node, {type: "edit"}, eventData); }; /** * [ext-edit] Stop inline editing. * @param {Boolean} [applyChanges=false] false: cancel edit, true: save (if modified) * @alias FancytreeNode#editEnd * @requires jquery.fancytree.edit.js */ $.ui.fancytree._FancytreeNodeClass.prototype.editEnd = function(applyChanges, _event){ var newVal, node = this, tree = this.tree, local = tree.ext.edit, eventData = local.eventData, instOpts = tree.options.edit, $title = $(".fancytree-title", node.span), $input = $title.find("input.fancytree-edit-input"); if( instOpts.trim ) { $input.val($.trim($input.val())); } newVal = $input.val(); eventData.dirty = ( newVal !== node.title ); eventData.originalEvent = _event; // Find out, if saving is required if( applyChanges === false ) { // If true/false was passed, honor this (except in rename mode, if unchanged) eventData.save = false; } else if( eventData.isNew ) { // In create mode, we save everything, except for empty text eventData.save = (newVal !== ""); } else { // In rename mode, we save everyting, except for empty or unchanged text eventData.save = eventData.dirty && (newVal !== ""); } // Allow to break (keep editor open), modify input, or re-define data.save if( instOpts.beforeClose.call(node, {type: "beforeClose"}, eventData) === false){ return false; } if( eventData.save && instOpts.save.call(node, {type: "save"}, eventData) === false){ return false; } $input .removeClass("fancytree-edit-dirty") .off(); // Unbind outer-click handler $(document).off(".fancytree-edit"); if( eventData.save ) { // # 171: escape user input (not required if global escaping is on) node.setTitle( tree.options.escapeTitles ? newVal : escapeHtml(newVal) ); node.setFocus(); }else{ if( eventData.isNew ) { node.remove(); node = eventData.node = null; local.relatedNode.setFocus(); } else { node.renderTitle(); node.setFocus(); } } local.eventData = null; local.currentNode = null; local.relatedNode = null; // Re-enable mouse and keyboard handling tree.widget._bind(); // Set keyboard focus, even if setFocus() claims 'nothing to do' $(tree.$container).focus(); eventData.input = null; instOpts.close.call(node, {type: "close"}, eventData); return true; }; /** * [ext-edit] Create a new child or sibling node and start edit mode. * * @param {String} [mode='child'] 'before', 'after', or 'child' * @param {Object} [init] NodeData (or simple title string) * @alias FancytreeNode#editCreateNode * @requires jquery.fancytree.edit.js * @since 2.4 */ $.ui.fancytree._FancytreeNodeClass.prototype.editCreateNode = function(mode, init){ var newNode, tree = this.tree, self = this; mode = mode || "child"; if( init == null ) { init = { title: "" }; } else if( typeof init === "string" ) { init = { title: init }; } else { $.ui.fancytree.assert($.isPlainObject(init)); } // Make sure node is expanded (and loaded) in 'child' mode if( mode === "child" && !this.isExpanded() && this.hasChildren() !== false ) { this.setExpanded().done(function(){ self.editCreateNode(mode, init); }); return; } newNode = this.addNode(init, mode); // #644: Don't filter new nodes. newNode.match = true; $(newNode[tree.statusClassPropName]) .removeClass("fancytree-hide") .addClass("fancytree-match"); newNode.makeVisible(/*{noAnimation: true}*/).done(function(){ $(newNode[tree.statusClassPropName]).addClass("fancytree-edit-new"); self.tree.ext.edit.relatedNode = self; newNode.editStart(); }); }; /** * [ext-edit] Check if any node in this tree in edit mode. * * @returns {FancytreeNode | null} * @alias Fancytree#isEditing * @requires jquery.fancytree.edit.js */ $.ui.fancytree._FancytreeClass.prototype.isEditing = function(){ return this.ext.edit ? this.ext.edit.currentNode : null; }; /** * [ext-edit] Check if this node is in edit mode. * @returns {Boolean} true if node is currently beeing edited * @alias FancytreeNode#isEditing * @requires jquery.fancytree.edit.js */ $.ui.fancytree._FancytreeNodeClass.prototype.isEditing = function(){ return this.tree.ext.edit ? this.tree.ext.edit.currentNode === this : false; }; /******************************************************************************* * Extension code */ $.ui.fancytree.registerExtension({ name: "edit", version: "2.30.0", // Default options for this extension. options: { adjustWidthOfs: 4, // null: don't adjust input size to content allowEmpty: false, // Prevent empty input inputCss: {minWidth: "3em"}, // triggerCancel: ["esc", "tab", "click"], triggerStart: ["f2", "mac+enter", "shift+click"], trim: true, // Trim whitespace before save // Events: beforeClose: $.noop, // Return false to prevent cancel/save (data.input is available) beforeEdit: $.noop, // Return false to prevent edit mode close: $.noop, // Editor was removed edit: $.noop, // Editor was opened (available as data.input) // keypress: $.noop, // Not yet implemented save: $.noop // Save data.input.val() or return false to keep editor open }, // Local attributes currentNode: null, treeInit: function(ctx){ this._superApply(arguments); this.$container.addClass("fancytree-ext-edit"); }, nodeClick: function(ctx) { var eventStr = $.ui.fancytree.eventToString(ctx.originalEvent), triggerStart = ctx.options.edit.triggerStart; if( eventStr === "shift+click" && $.inArray("shift+click", triggerStart) >= 0 ){ if( ctx.originalEvent.shiftKey ){ ctx.node.editStart(); return false; } } if( eventStr === "click" && $.inArray("clickActive", triggerStart) >= 0 ){ // Only when click was inside title text (not aynwhere else in the row) if( ctx.node.isActive() && !ctx.node.isEditing() && $(ctx.originalEvent.target).hasClass("fancytree-title") ){ ctx.node.editStart(); return false; } } return this._superApply(arguments); }, nodeDblclick: function(ctx) { if( $.inArray("dblclick", ctx.options.edit.triggerStart) >= 0 ){ ctx.node.editStart(); return false; } return this._superApply(arguments); }, nodeKeydown: function(ctx) { switch( ctx.originalEvent.which ) { case 113: // [F2] if( $.inArray("f2", ctx.options.edit.triggerStart) >= 0 ){ ctx.node.editStart(); return false; } break; case $.ui.keyCode.ENTER: if( $.inArray("mac+enter", ctx.options.edit.triggerStart) >= 0 && isMac ){ ctx.node.editStart(); return false; } break; } return this._superApply(arguments); } }); // Value returned by `require('jquery.fancytree..')` return $.ui.fancytree; })); // End of closure