diff --git a/.gitignore b/.gitignore index 1063fcb..fb455ee 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ test/tmp test/version_tmp tmp /tags +.idea diff --git a/3th-party/jquery-ui.License.txt b/3rd-party/jquery-ui.License.txt similarity index 100% rename from 3th-party/jquery-ui.License.txt rename to 3rd-party/jquery-ui.License.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 68caed6..b571a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v2.4.1 + + * use fancytree v. 2.4.1 + * stylesheets converted to SCSS and does not use ERB anymore + * minimized and concatenated file versions removed in favor of asset pipeline + * skins are working + ## v2.3.0 * use fancytree v. 2.3.0 diff --git a/README.md b/README.md index ea6311f..4d90e18 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,15 @@ # Fancytree::Rails -fancytree-rails is a simple gem to add [FancyTree](https://github.com/mar10/fancytree) vendor files to the +`fancytree-rails` is a simple gem to add [FancyTree](https://github.com/mar10/fancytree) JavaScript library files to the Rails asset pipeline. ## Installation Add this line to your application's Gemfile: - gem 'fancytree-rails' +```ruby +gem 'fancytree-rails' +``` And then execute: @@ -19,39 +21,33 @@ Or install it yourself as: ## Usage -In your Gemfile, add: - +Note that [jquery-ui-rails](https://github.com/joliss/jquery-ui-rails) also needs to be available. -```ruby -gem 'fancytree-rails' -``` +Require all javascript (core and all extensions) by adding the following to `app/assets/javascripts/application.js`: -Note that also [jquery-ui-rails](https://github.com/joliss/jquery-ui-rails) needs to be available. - -Require javascript to ```app/assets/javascripts/application.js```: - -``` +```js //= require fancytree ``` -In case you want to use *gridnav* add also ```//=require fancytree/jquery.fancytree.gridnav.js``` +Or you can add core and required extensions only, for example: +```js +//= require fancytree/jquery.fancytree-core +//= require fancytree/src/jquery.fancytree.glyph +//= require fancytree/src/jquery.fancytree.gridnav +``` -Extend stylesheet assets ```app/assets/stylesheets/application.css``` -by: +Extend stylesheet assets (defaults to the `lion` skin) by adding the following to `app/assets/stylesheets/application.css`: -``` +```css *= require ui.fancytree ``` -or if you want to use another style include it directly: -``` -*= require fancytree/skin-awesome/ui.fancytree +If you want to use another skin, include it's stylesheet instead: +```css +*= require fancytree/skin-vista/ui.fancytree ``` -Note that for awesome style (extension) to work you to: -1. Add ```font-awesome-rails``` gem (and include its style in application.css) -2. Enable ```awesome``` extension in Fancytree. -3. Include extension code (```//= require fancytree/jquery.fancytree.awesome```) to ```app/assets/javascripts/application.js```. +Note that for `awesome` or `bootatrap` skin to work you need to use `font-awesome-rails` or [one](https://github.com/twbs/bootstrap-sass) [of](https://github.com/seyhunak/twitter-bootstrap-rails) the `bootstrap` gems accordingly. ## Credits diff --git a/app/assets/images/fancytree/icons.gif b/app/assets/images/fancytree/icons.gif deleted file mode 100644 index 6e237a0..0000000 Binary files a/app/assets/images/fancytree/icons.gif and /dev/null differ diff --git a/app/assets/images/fancytree/loading.gif b/app/assets/images/fancytree/loading.gif deleted file mode 100644 index ef88497..0000000 Binary files a/app/assets/images/fancytree/loading.gif and /dev/null differ diff --git a/app/assets/javascripts/fancytree.js b/app/assets/javascripts/fancytree.js index 423a52b..953a021 100644 --- a/app/assets/javascripts/fancytree.js +++ b/app/assets/javascripts/fancytree.js @@ -1,2 +1,2 @@ //= require jquery -//= require fancytree/jquery.fancytree-all.js +//= require fancytree/jquery.fancytree-all diff --git a/app/assets/javascripts/fancytree/jquery.fancytree-all.js b/app/assets/javascripts/fancytree/jquery.fancytree-all.js index 85a1ac4..48b9d68 100644 --- a/app/assets/javascripts/fancytree/jquery.fancytree-all.js +++ b/app/assets/javascripts/fancytree/jquery.fancytree-all.js @@ -1,6882 +1,14 @@ -/*! - * jquery.fancytree.js - * Dynamic tree view control, with support for lazy loading of branches. - * https://github.com/mar10/fancytree/ - * - * Copyright (c) 2006-2014, Martin Wendt (http://wwWendt.de) - * Released under the MIT license - * https://github.com/mar10/fancytree/wiki/LicenseInfo - * - * @version 2.3.0 - * @date 2014-08-17T10:39 - */ - -/** Core Fancytree module. - */ - - -// Start of local namespace -;(function($, window, document, undefined) { -"use strict"; - -// prevent duplicate loading -if ( $.ui.fancytree && $.ui.fancytree.version ) { - $.ui.fancytree.warn("Fancytree: ignored duplicate include"); - return; -} - - -/* ***************************************************************************** - * Private functions and variables - */ - -function _raiseNotImplemented(msg){ - msg = msg || ""; - $.error("Not implemented: " + msg); -} - -function _assert(cond, msg){ - // TODO: see qunit.js extractStacktrace() - if(!cond){ - msg = msg ? ": " + msg : ""; - $.error("Assertion failed" + msg); - } -} - -function consoleApply(method, args){ - var i, s, - fn = window.console ? window.console[method] : null; - - if(fn){ - if(fn.apply){ - fn.apply(window.console, args); - }else{ - // IE? - s = ""; - for( i=0; i t ); - } - } - return true; -} - -/** Return a wrapper that calls sub.methodName() and exposes - * this : tree - * this._local : tree.ext.EXTNAME - * this._super : base.methodName() - */ -function _makeVirtualFunction(methodName, tree, base, extension, extName){ - // $.ui.fancytree.debug("_makeVirtualFunction", methodName, tree, base, extension, extName); - // if(rexTestSuper && !rexTestSuper.test(func)){ - // // extension.methodName() doesn't call _super(), so no wrapper required - // return func; - // } - // Use an immediate function as closure - var proxy = (function(){ - var prevFunc = tree[methodName], // org. tree method or prev. proxy - baseFunc = extension[methodName], // - _local = tree.ext[extName], - _super = function(){ - return prevFunc.apply(tree, arguments); - }; - - // Return the wrapper function - return function(){ - var prevLocal = tree._local, - prevSuper = tree._super; - try{ - tree._local = _local; - tree._super = _super; - return baseFunc.apply(tree, arguments); - }finally{ - tree._local = prevLocal; - tree._super = prevSuper; - } - }; - })(); // end of Immediate Function - return proxy; -} - -/** - * Subclass `base` by creating proxy functions - */ -function _subclassObject(tree, base, extension, extName){ - // $.ui.fancytree.debug("_subclassObject", tree, base, extension, extName); - for(var attrName in extension){ - if(typeof extension[attrName] === "function"){ - if(typeof tree[attrName] === "function"){ - // override existing method - tree[attrName] = _makeVirtualFunction(attrName, tree, base, extension, extName); - }else if(attrName.charAt(0) === "_"){ - // Create private methods in tree.ext.EXTENSION namespace - tree.ext[extName][attrName] = _makeVirtualFunction(attrName, tree, base, extension, extName); - }else{ - $.error("Could not override tree." + attrName + ". Use prefix '_' to create tree." + extName + "._" + attrName); - } - }else{ - // Create member variables in tree.ext.EXTENSION namespace - if(attrName !== "options"){ - tree.ext[extName][attrName] = extension[attrName]; - } - } - } -} - - -function _getResolvedPromise(context, argArray){ - if(context === undefined){ - return $.Deferred(function(){this.resolve();}).promise(); - }else{ - return $.Deferred(function(){this.resolveWith(context, argArray);}).promise(); - } -} - - -function _getRejectedPromise(context, argArray){ - if(context === undefined){ - return $.Deferred(function(){this.reject();}).promise(); - }else{ - return $.Deferred(function(){this.rejectWith(context, argArray);}).promise(); - } -} - - -function _makeResolveFunc(deferred, context){ - return function(){ - deferred.resolveWith(context); - }; -} - - -function _getElementDataAsDict($el){ - // Evaluate 'data-NAME' attributes with special treatment for 'data-json'. - var d = $.extend({}, $el.data()), - json = d.json; - delete d.fancytree; // added to container by widget factory - if( json ) { - delete d.json; - //
  • is already returned as object (http://api.jquery.com/data/#data-html5) - d = $.extend(d, json); - } - return d; -} - - -// TODO: use currying -function _makeNodeTitleMatcher(s){ - s = s.toLowerCase(); - return function(node){ - return node.title.toLowerCase().indexOf(s) >= 0; - }; -} - -var i, - FT = null, // initialized below - ENTITY_MAP = {"&": "&", "<": "<", ">": ">", "\"": """, "'": "'", "/": "/"}, - //boolean attributes that can be set with equivalent class names in the LI tags - CLASS_ATTRS = "active expanded focus folder hideCheckbox lazy selected unselectable".split(" "), - CLASS_ATTR_MAP = {}, - // Top-level Fancytree node attributes, that can be set by dict - NODE_ATTRS = "expanded extraClasses folder hideCheckbox key lazy refKey selected title tooltip unselectable".split(" "), - NODE_ATTR_MAP = {}, - // Attribute names that should NOT be added to node.data - NONE_NODE_DATA_MAP = {"active": true, "children": true, "data": true, "focus": true}; - -for(i=0; i - * For lazy nodes, null or undefined means 'not yet loaded'. Use an empty array - * to define a node that has no children. - * @property {boolean} expanded Use isExpanded(), setExpanded() to access this property. - * @property {string} extraClasses Addtional CSS classes, added to the node's `<span>` - * @property {boolean} folder Folder nodes have different default icons and click behavior.
    - * Note: Also non-folders may have children. - * @property {string} statusNodeType null or type of temporarily generated system node like 'loading', or 'error'. - * @property {boolean} lazy True if this node is loaded on demand, i.e. on first expansion. - * @property {boolean} selected Use isSelected(), setSelected() to access this property. - * @property {string} tooltip Alternative description used as hover banner - */ -function FancytreeNode(parent, obj){ - var i, l, name, cl; - - this.parent = parent; - this.tree = parent.tree; - this.ul = null; - this.li = null; //
  • tag - this.statusNodeType = null; // if this is a temp. node to display the status of its parent - this._isLoading = false; // if this node itself is loading - this._error = null; // {message: '...'} if a load error occured - this.data = {}; - - // TODO: merge this code with node.toDict() - // copy attributes from obj object - for(i=0, l=NODE_ATTRS.length; i= 0, "insertBefore must be an existing child"); - // insert nodeList after children[pos] - this.children.splice.apply(this.children, [pos, 0].concat(nodeList)); - } - if( !this.parent || this.parent.ul || this.tr ){ - // render if the parent was rendered (or this is a root node) - this.render(); - } - if( this.tree.options.selectMode === 3 ){ - this.fixSelection3FromEndNodes(); - } - return firstNode; - }, - /** - * Append or prepend a node, or append a child node. - * - * This a convenience function that calls addChildren() - * - * @param {NodeData} node node definition - * @param {string} [mode=child] 'before', 'after', or 'child' ('over' is a synonym for 'child') - * @returns {FancytreeNode} new node - */ - addNode: function(node, mode){ - if(mode === undefined || mode === "over"){ - mode = "child"; - } - switch(mode){ - case "after": - return this.getParent().addChildren(node, this.getNextSibling()); - case "before": - return this.getParent().addChildren(node, this); - case "child": - case "over": - return this.addChildren(node); - } - _assert(false, "Invalid mode: " + mode); - }, - /** - * Append new node after this. - * - * This a convenience function that calls addNode(node, 'after') - * - * @param {NodeData} node node definition - * @returns {FancytreeNode} new node - */ - appendSibling: function(node){ - return this.addNode(node, "after"); - }, - /** - * Modify existing child nodes. - * - * @param {NodePatch} patch - * @returns {$.Promise} - * @see FancytreeNode#addChildren - */ - applyPatch: function(patch) { - // patch [key, null] means 'remove' - if(patch === null){ - this.remove(); - return _getResolvedPromise(this); - } - // TODO: make sure that root node is not collapsed or modified - // copy (most) attributes to node.ATTR or node.data.ATTR - var name, promise, v, - IGNORE_MAP = { children: true, expanded: true, parent: true }; // TODO: should be global - - for(name in patch){ - v = patch[name]; - if( !IGNORE_MAP[name] && !$.isFunction(v)){ - if(NODE_ATTR_MAP[name]){ - this[name] = v; - }else{ - this.data[name] = v; - } - } - } - // Remove and/or create children - if(patch.hasOwnProperty("children")){ - this.removeChildren(); - if(patch.children){ // only if not null and not empty list - // TODO: addChildren instead? - this._setChildren(patch.children); - } - // TODO: how can we APPEND or INSERT child nodes? - } - if(this.isVisible()){ - this.renderTitle(); - this.renderStatus(); - } - // Expand collapse (final step, since this may be async) - if(patch.hasOwnProperty("expanded")){ - promise = this.setExpanded(patch.expanded); - }else{ - promise = _getResolvedPromise(this); - } - return promise; - }, - /** Collapse all sibling nodes. - * @returns {$.Promise} - */ - collapseSiblings: function() { - return this.tree._callHook("nodeCollapseSiblings", this); - }, - /** Copy this node as sibling or child of `node`. - * - * @param {FancytreeNode} node source node - * @param {string} mode 'before' | 'after' | 'child' - * @param {Function} [map] callback function(NodeData) that could modify the new node - * @returns {FancytreeNode} new - */ - copyTo: function(node, mode, map) { - return node.addNode(this.toDict(true, map), mode); - }, - /** Count direct and indirect children. - * - * @param {boolean} [deep=true] pass 'false' to only count direct children - * @returns {int} number of child nodes - */ - countChildren: function(deep) { - var cl = this.children, i, l, n; - if( !cl ){ - return 0; - } - n = cl.length; - if(deep !== false){ - for(i=0, l=n; i= 2 (prepending node info) - * - * @param {*} msg string or object or array of such - */ - debug: function(msg){ - if( this.tree.options.debugLevel >= 2 ) { - Array.prototype.unshift.call(arguments, this.toString()); - consoleApply("debug", arguments); - } - }, - /** Deprecated. - * @deprecated since 2014-02-16. Use resetLazy() instead. - */ - discard: function(){ - this.warn("FancytreeNode.discard() is deprecated since 2014-02-16. Use .resetLazy() instead."); - return this.resetLazy(); - }, - // TODO: expand(flag) - /**Find all nodes that contain `match` in the title. - * - * @param {string | function(node)} match string to search for, of a function that - * returns `true` if a node is matched. - * @returns {FancytreeNode[]} array of nodes (may be empty) - * @see FancytreeNode#findAll - */ - findAll: function(match) { - match = $.isFunction(match) ? match : _makeNodeTitleMatcher(match); - var res = []; - this.visit(function(n){ - if(match(n)){ - res.push(n); - } - }); - return res; - }, - /**Find first node that contains `match` in the title (not including self). - * - * @param {string | function(node)} match string to search for, of a function that - * returns `true` if a node is matched. - * @returns {FancytreeNode} matching node or null - * @example - * fat text - */ - findFirst: function(match) { - match = $.isFunction(match) ? match : _makeNodeTitleMatcher(match); - var res = null; - this.visit(function(n){ - if(match(n)){ - res = n; - return false; - } - }); - return res; - }, - /* Apply selection state (internal use only) */ - _changeSelectStatusAttrs: function (state) { - var changed = false; - - switch(state){ - case false: - changed = ( this.selected || this.partsel ); - this.selected = false; - this.partsel = false; - break; - case true: - changed = ( !this.selected || !this.partsel ); - this.selected = true; - this.partsel = true; - break; - case undefined: - changed = ( this.selected || !this.partsel ); - this.selected = false; - this.partsel = true; - break; - default: - _assert(false, "invalid state: " + state); - } - // this.debug("fixSelection3AfterLoad() _changeSelectStatusAttrs()", state, changed); - if( changed ){ - this.renderStatus(); - } - return changed; - }, - /** - * Fix selection status, after this node was (de)selected in multi-hier mode. - * This includes (de)selecting all children. - */ - fixSelection3AfterClick: function() { - var flag = this.isSelected(); - -// this.debug("fixSelection3AfterClick()"); - - this.visit(function(node){ - node._changeSelectStatusAttrs(flag); - }); - this.fixSelection3FromEndNodes(); - }, - /** - * Fix selection status for multi-hier mode. - * Only end-nodes are considered to update the descendants branch and parents. - * Should be called after this node has loaded new children or after - * children have been modified using the API. - */ - fixSelection3FromEndNodes: function() { -// this.debug("fixSelection3FromEndNodes()"); - _assert(this.tree.options.selectMode === 3, "expected selectMode 3"); - - // Visit all end nodes and adjust their parent's `selected` and `partsel` - // attributes. Return selection state true, false, or undefined. - function _walk(node){ - var i, l, child, s, state, allSelected,someSelected, - children = node.children; - - if( children && children.length ){ - // check all children recursively - allSelected = true; - someSelected = false; - - for( i=0, l=children.length; i= 1 (prepending node info) - * - * @param {*} msg string or object or array of such - */ - info: function(msg){ - if( this.tree.options.debugLevel >= 1 ) { - Array.prototype.unshift.call(arguments, this.toString()); - consoleApply("info", arguments); - } - }, - /** Return true if node is active (see also FancytreeNode#isSelected). - * @returns {boolean} - */ - isActive: function() { - return (this.tree.activeNode === this); - }, - /** Return true if node is a direct child of otherNode. - * @param {FancytreeNode} otherNode - * @returns {boolean} - */ - isChildOf: function(otherNode) { - return (this.parent && this.parent === otherNode); - }, - /** Return true, if node is a direct or indirect sub node of otherNode. - * @param {FancytreeNode} otherNode - * @returns {boolean} - */ - isDescendantOf: function(otherNode) { - if(!otherNode || otherNode.tree !== this.tree){ - return false; - } - var p = this.parent; - while( p ) { - if( p === otherNode ){ - return true; - } - p = p.parent; - } - return false; - }, - /** Return true if node is expanded. - * @returns {boolean} - */ - isExpanded: function() { - return !!this.expanded; - }, - /** Return true if node is the first node of its parent's children. - * @returns {boolean} - */ - isFirstSibling: function() { - var p = this.parent; - return !p || p.children[0] === this; - }, - /** Return true if node is a folder, i.e. has the node.folder attribute set. - * @returns {boolean} - */ - isFolder: function() { - return !!this.folder; - }, - /** Return true if node is the last node of its parent's children. - * @returns {boolean} - */ - isLastSibling: function() { - var p = this.parent; - return !p || p.children[p.children.length-1] === this; - }, - /** Return true if node is lazy (even if data was already loaded) - * @returns {boolean} - */ - isLazy: function() { - return !!this.lazy; - }, - /** Return true if node is lazy and loaded. For non-lazy nodes always return true. - * @returns {boolean} - */ - isLoaded: function() { - return !this.lazy || this.hasChildren() !== undefined; // Also checks if the only child is a status node - }, - /** Return true if children are currently beeing loaded, i.e. a Ajax request is pending. - * @returns {boolean} - */ - isLoading: function() { - return !!this._isLoading; - }, - /** Return true if this is the (invisible) system root node. - * @returns {boolean} - */ - isRoot: function() { - return (this.tree.rootNode === this); - }, - /** Return true if node is selected, i.e. has a checkmark set (see also FancytreeNode#isActive). - * @returns {boolean} - */ - isSelected: function() { - return !!this.selected; - }, - /** Return true if this node is a temporarily generated system node like - * 'loading', or 'error' (node.statusNodeType contains the type). - * @returns {boolean} - */ - isStatusNode: function() { - return !!this.statusNodeType; - }, - /** Return true if node is lazy and not yet loaded. For non-lazy nodes always return false. - * @returns {boolean} - */ - isUndefined: function() { - return this.hasChildren() === undefined; // also checks if the only child is a status node - }, - /** Return true if all parent nodes are expanded. Note: this does not check - * whether the node is scrolled into the visible part of the screen. - * @returns {boolean} - */ - isVisible: function() { - var i, l, - parents = this.getParentList(false, false); - - for(i=0, l=parents.length; i= 0; i--){ - // that.debug("pushexpand" + parents[i]); - deferreds.push(parents[i].setExpanded(true, opts)); - } - $.when.apply($, deferreds).done(function(){ - // All expands have finished - // that.debug("expand DONE", scroll); - if( scroll ){ - that.scrollIntoView(effects).done(function(){ - // that.debug("scroll DONE"); - dfd.resolve(); - }); - } else { - dfd.resolve(); - } - }); - return dfd.promise(); - }, - /** Move this node to targetNode. - * @param {FancytreeNode} targetNode - * @param {string} mode
    -	 *      'child': append this node as last child of targetNode.
    -	 *               This is the default. To be compatble with the D'n'd
    -	 *               hitMode, we also accept 'over'.
    -	 *      'before': add this node as sibling before targetNode.
    -	 *      'after': add this node as sibling after targetNode.
    - * @param {function} [map] optional callback(FancytreeNode) to allow modifcations - */ - moveTo: function(targetNode, mode, map) { - if(mode === undefined || mode === "over"){ - mode = "child"; - } - var pos, - prevParent = this.parent, - targetParent = (mode === "child") ? targetNode : targetNode.parent; - - if(this === targetNode){ - return; - }else if( !this.parent ){ - throw "Cannot move system root"; - }else if( targetParent.isDescendantOf(this) ){ - throw "Cannot move a node to its own descendant"; - } - // Unlink this node from current parent - if( this.parent.children.length === 1 ) { - if( this.parent === targetParent ){ - return; // #258 - } - this.parent.children = this.parent.lazy ? [] : null; - this.parent.expanded = false; - } else { - pos = $.inArray(this, this.parent.children); - _assert(pos >= 0); - this.parent.children.splice(pos, 1); - } - // Remove from source DOM parent -// if(this.parent.ul){ -// this.parent.ul.removeChild(this.li); -// } - - // Insert this node to target parent's child list - this.parent = targetParent; - if( targetParent.hasChildren() ) { - switch(mode) { - case "child": - // Append to existing target children - targetParent.children.push(this); - break; - case "before": - // Insert this node before target node - pos = $.inArray(targetNode, targetParent.children); - _assert(pos >= 0); - targetParent.children.splice(pos, 0, this); - break; - case "after": - // Insert this node after target node - pos = $.inArray(targetNode, targetParent.children); - _assert(pos >= 0); - targetParent.children.splice(pos+1, 0, this); - break; - default: - throw "Invalid mode " + mode; - } - } else { - targetParent.children = [ this ]; - } - // Parent has no