/*! * jquery.fancytree.edit.js * * Make node titles editable. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/) * * Copyright (c) 2008-2017, Martin Wendt (http://wwWendt.de) * * Released under the MIT license * https://github.com/mar10/fancytree/wiki/LicenseInfo * * @version 2.23.0 * @date 2017-05-27T20:09:38Z */ ;(function($, window, document, undefined) { "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 = $("", { "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 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 everyting, 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.23.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", "dblclick", "shift+click", "mac+enter"], triggerStart: ["f2", "shift+click", "mac+enter"], 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) { if( $.inArray("shift+click", ctx.options.edit.triggerStart) >= 0 ){ if( ctx.originalEvent.shiftKey ){ 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); } }); }(jQuery, window, document));