/* ============================================================= * bootstrap-tree.js v0.3 * http://twitter.github.com/cutterbl/Bootstrap-Tree * * Inspired by Twitter Bootstrap, with credit to bits of code * from all over. * ============================================================= * Copyright 2012 Cutters Crossing. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================ */ !function ($) { "use strict"; // jshint ;_; var loading = "<img src='assets/plugins/bootstrap-tree/bootstrap-tree/img/ajax-loader.gif' class='indicator' /> Loading ..."; /* TREE CLASS DEFINITION * ========================= */ var Tree = function (element, options) { this.$element = $(element) this.$tree = this.$element.closest(".tree") this.parentage = GetParentage(this.$element) this.options = $.extend({}, $.fn.tree.defaults, options) if (this.options.parent) { this.$parent = $(this.options.parent) } this.options.toggle && this.toggle() } Tree.prototype = { constructor: Tree , toggle: function () { var a, n, s , currentStatus = this.$element.hasClass("in") , eventName = (!currentStatus) ? "openbranch" : "closebranch" this.$parent[currentStatus ? "addClass" : "removeClass"]("closed") this.$element[currentStatus ? "removeClass" : "addClass"]("in") if (this.options.href) { this._load() } n = this.node() // 'Action' (open|close) event a = $.Event(eventName, { node: n }) // 'Select' event s = $.Event("nodeselect", { node: n }) this.$parent.trigger(a).trigger(s) } , _load: function () { var data = $.extend({}, this.options) , el = this.$element , $this = $(this) , options = this.options // some config data we don't need to pass in the post delete data.parent delete data.href delete data.callback $.post(options.href, data, function (d, s, x){ var doc, type = "html" if (options.callback) { // If a callback was defined in the data parameters var cb = window[options.callback].apply(el, [d, s, x]) // callbacks must return an object with 'doc' and 'type' keys doc = cb.doc || d type = cb.type || type } else { try { doc = $.parseJSON(d) type = "json" } catch (err) { doc = d } if (type !== "json") { try { doc = $.parseXML(d) type = "xml" } catch (err) { doc = d } } } switch (type) { case "html": el.html(doc) break default: $this[0]._buildOutput(doc, type, el) break } }, "html") } , _buildOutput: function (doc, type, parent) { var nodes = this._buildNodes(doc, type) parent.empty().append(this._createNodes(nodes)) } , _createNodes: function (nodes) { var els = [] , $this = $(this) $.each(nodes, function (ind, el) { var node = $("<li>") , role = (el.leaf) ? "leaf" : "branch" , attributes = {} , anchor = $("<a>") attributes.role = role if (!el.leaf) { var branch = $("<ul>").addClass("branch") attributes['class'] = "tree-toggle closed" //fixed by keenthemes for ie8 attributes["data-toggle"] = "branch" } if (el.value) attributes["data-value"] = el.value if (el.id) attributes["data-itemid"] = el.id for (var key in el) { // do we have some extras? if (key.indexOf("data-") !== -1) attributes[key] = el[key] } attributes.href = (el.href) ? el.href : "#" // trade the anchor for a span tag, if it's a leaf // and there's no href if (el.leaf && attributes.href === "#") { anchor = $("<span>") delete attributes.href } anchor.attr(attributes) if (el.cls) anchor.addClass(el.cls) if (!el.leaf && el.expanded && el.children.length) { anchor.removeClass("closed") branch.addClass("in") } anchor.html(el.text) node.append(anchor) if (!el.leaf && el.children && el.children.length) { branch.append($this[0]._createNodes(el.children)) node.append(branch) } els.push(node) }) return els } , _buildNodes: function (doc, type) { var nodes = [] , $el = this.$element if (type === "json") { nodes = this._parseJsonNodes(doc) } else if (type === "xml") { nodes = this._parseXmlNodes($(doc).find("nodes").children()) } return nodes } , _parseJsonNodes: function (doc) { var nodes = [] , $this = $(this) $.each(doc, function (ind, el) { var opts = {} , boolChkArr = ["leaf","expanded","checkable","checked"] for (var item in el) { var nodeVal = (item !== "children") ? el[item] : $this[0]._parseJsonNodes(el.children) if (!$.isArray(nodeVal)) nodeVal = $.trim(nodeVal) if (nodeVal.length) opts[item] = ($.inArray(item, boolChkArr) > -1) ? SetBoolean(nodeVal) : nodeVal } nodes.push(new Node(opts)) }) return nodes } , _parseXmlNodes: function (doc) { var nodes = [] , $this = $(this) , boolChkArr = ["leaf","expanded","checkable","checked"] $.each(doc, function (ind, el) { var opts = {} , $el = $(el) $.each($el.children(), function (x, i) { var $i = $(i) , tagName = $i[0].nodeName , nodeVal = (tagName !== "children") ? $i.text() : $this[0]._parseXmlNodes($i.children("node")) if (!$.isArray(nodeVal)) nodeVal = $.trim(nodeVal) if (nodeVal.length) opts[tagName] = ($.inArray(tagName, boolChkArr) > -1) ? SetBoolean(nodeVal) : nodeVal }) nodes.push(new Node(opts)) }) return nodes } , getparentage: function () { return this.parentage } , node: function (el) { el = el || $(this) var node = $.extend(true, {}, (el[0] === $(this)[0]) ? $(this.$parent).data() : el.data()) node.branch = this.$element node.parentage = this.parentage node.el = (el[0] === $(this)[0]) ? this.$parent : el delete node.parent return node } } var Node = function (options) { $.extend(true, this, { text: undefined, leaf: false, value: undefined, expanded: false, cls: undefined, id: undefined, href: undefined, checkable: false, checked: false, children: [] }, options) } var GetParentBranch = function ($this) { return $this.closest("ul.branch").prev(".tree-toggle") } var GetParentage = function ($this) { var arr = [], tmp tmp = GetParentBranch($this) if (tmp.length) { arr = GetParentage(tmp) arr.push(tmp.attr("data-value")||tmp.text()) } return arr } /** * FUNCTION SetBoolean * * Takes any value, and returns it's boolean equivalent. * * @param value (any) * @return (boolean) */ var SetBoolean = function (value) { value = $.trim(value) if (typeof value === "undefined" || value === null) return false if (typeof value === "string" && !isNaN(value)) value = parseFloat(value) if (typeof value === "string") { switch (value.toLowerCase()) { case "true": case "yes": return true case "false": case "no": return false } } return Boolean(value) } /* COLLAPSIBLE PLUGIN DEFINITION * ============================== */ $.fn.tree = function (option) { return this.each(function () { var $this = $(this) , data = $this.data("tree") , options = typeof option == "object" && option if (!data) $this.data("tree", (data = new Tree(this, options))) if (typeof option == "string") data[option]() }) } $.fn.tree.defaults = { toggle: true } $.fn.tree.Constructor = Tree /* COLLAPSIBLE DATA-API * ==================== */ $(function () { $("body").on("click.tree.data-api", "[data-toggle=branch]", function (e) { e.preventDefault() var $this = $(this) , target = $this.next(".branch") , href = $this.attr("href") , option = $(target).data("tree") ? "toggle" : $this.data() href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 if (!target.length) { target = $('<ul>').addClass('branch').append("<li>" + loading + "</li>").insertAfter($this) } option.parent = $this option.href = (href !== "#") ? href : undefined $(target).tree(option) return false }) $("body").on("click.tree.data-api", "[data-role=leaf]", function (e) { var $this = $(this) , branch = $this.closest(".branch") // If not initialized, then create it if (!$(branch).data("tree")) { var $target = $(branch) , branchlink = $target.prev("[data-toggle=branch]") , branchdata = branchlink.data() , href = branchlink.attr("href") href.replace(/.*(?=#[^\s]+$)/, '') $target.tree($.extend({}, branchdata, { "toggle": false, "parent": branchlink, "href": (href !== "#") ? href : undefined })) } e = $.Event("nodeselect", { node: $(branch).data("tree").node($this) }) $this.trigger(e) }) }) }(window.jQuery);