bootstrap-tree.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. /* =============================================================
  2. * bootstrap-tree.js v0.3
  3. * http://twitter.github.com/cutterbl/Bootstrap-Tree
  4. *
  5. * Inspired by Twitter Bootstrap, with credit to bits of code
  6. * from all over.
  7. * =============================================================
  8. * Copyright 2012 Cutters Crossing.
  9. *
  10. * Licensed under the Apache License, Version 2.0 (the "License");
  11. * you may not use this file except in compliance with the License.
  12. * You may obtain a copy of the License at
  13. *
  14. * http://www.apache.org/licenses/LICENSE-2.0
  15. *
  16. * Unless required by applicable law or agreed to in writing, software
  17. * distributed under the License is distributed on an "AS IS" BASIS,
  18. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19. * See the License for the specific language governing permissions and
  20. * limitations under the License.
  21. * ============================================================ */
  22. !function ($) {
  23. "use strict"; // jshint ;_;
  24. var loading = "<img src='assets/plugins/bootstrap-tree/bootstrap-tree/img/ajax-loader.gif' class='indicator' /> Loading ...";
  25. /* TREE CLASS DEFINITION
  26. * ========================= */
  27. var Tree = function (element, options) {
  28. this.$element = $(element)
  29. this.$tree = this.$element.closest(".tree")
  30. this.parentage = GetParentage(this.$element)
  31. this.options = $.extend({}, $.fn.tree.defaults, options)
  32. if (this.options.parent) {
  33. this.$parent = $(this.options.parent)
  34. }
  35. this.options.toggle && this.toggle()
  36. }
  37. Tree.prototype = {
  38. constructor: Tree
  39. , toggle: function () {
  40. var a, n, s
  41. , currentStatus = this.$element.hasClass("in")
  42. , eventName = (!currentStatus) ? "openbranch" : "closebranch"
  43. this.$parent[currentStatus ? "addClass" : "removeClass"]("closed")
  44. this.$element[currentStatus ? "removeClass" : "addClass"]("in")
  45. if (this.options.href) {
  46. this._load()
  47. }
  48. n = this.node()
  49. // 'Action' (open|close) event
  50. a = $.Event(eventName, {
  51. node: n
  52. })
  53. // 'Select' event
  54. s = $.Event("nodeselect", {
  55. node: n
  56. })
  57. this.$parent.trigger(a).trigger(s)
  58. }
  59. , _load: function () {
  60. var data = $.extend({}, this.options)
  61. , el = this.$element
  62. , $this = $(this)
  63. , options = this.options
  64. // some config data we don't need to pass in the post
  65. delete data.parent
  66. delete data.href
  67. delete data.callback
  68. $.post(options.href, data, function (d, s, x){
  69. var doc, type = "html"
  70. if (options.callback) { // If a callback was defined in the data parameters
  71. var cb = window[options.callback].apply(el, [d, s, x]) // callbacks must return an object with 'doc' and 'type' keys
  72. doc = cb.doc || d
  73. type = cb.type || type
  74. } else {
  75. try {
  76. doc = $.parseJSON(d)
  77. type = "json"
  78. } catch (err) {
  79. doc = d
  80. }
  81. if (type !== "json") {
  82. try {
  83. doc = $.parseXML(d)
  84. type = "xml"
  85. } catch (err) {
  86. doc = d
  87. }
  88. }
  89. }
  90. switch (type) {
  91. case "html":
  92. el.html(doc)
  93. break
  94. default:
  95. $this[0]._buildOutput(doc, type, el)
  96. break
  97. }
  98. }, "html")
  99. }
  100. , _buildOutput: function (doc, type, parent) {
  101. var nodes = this._buildNodes(doc, type)
  102. parent.empty().append(this._createNodes(nodes))
  103. }
  104. , _createNodes: function (nodes) {
  105. var els = []
  106. , $this = $(this)
  107. $.each(nodes, function (ind, el) {
  108. var node = $("<li>")
  109. , role = (el.leaf) ? "leaf" : "branch"
  110. , attributes = {}
  111. , anchor = $("<a>")
  112. attributes.role = role
  113. if (!el.leaf) {
  114. var branch = $("<ul>").addClass("branch")
  115. attributes['class'] = "tree-toggle closed" //fixed by keenthemes for ie8
  116. attributes["data-toggle"] = "branch"
  117. }
  118. if (el.value) attributes["data-value"] = el.value
  119. if (el.id) attributes["data-itemid"] = el.id
  120. for (var key in el) { // do we have some extras?
  121. if (key.indexOf("data-") !== -1) attributes[key] = el[key]
  122. }
  123. attributes.href = (el.href) ? el.href : "#"
  124. // trade the anchor for a span tag, if it's a leaf
  125. // and there's no href
  126. if (el.leaf && attributes.href === "#") {
  127. anchor = $("<span>")
  128. delete attributes.href
  129. }
  130. anchor.attr(attributes)
  131. if (el.cls) anchor.addClass(el.cls)
  132. if (!el.leaf && el.expanded && el.children.length) {
  133. anchor.removeClass("closed")
  134. branch.addClass("in")
  135. }
  136. anchor.html(el.text)
  137. node.append(anchor)
  138. if (!el.leaf && el.children && el.children.length) {
  139. branch.append($this[0]._createNodes(el.children))
  140. node.append(branch)
  141. }
  142. els.push(node)
  143. })
  144. return els
  145. }
  146. , _buildNodes: function (doc, type) {
  147. var nodes = []
  148. , $el = this.$element
  149. if (type === "json") {
  150. nodes = this._parseJsonNodes(doc)
  151. } else if (type === "xml") {
  152. nodes = this._parseXmlNodes($(doc).find("nodes").children())
  153. }
  154. return nodes
  155. }
  156. , _parseJsonNodes: function (doc) {
  157. var nodes = []
  158. , $this = $(this)
  159. $.each(doc, function (ind, el) {
  160. var opts = {}
  161. , boolChkArr = ["leaf","expanded","checkable","checked"]
  162. for (var item in el) {
  163. var nodeVal = (item !== "children") ? el[item] : $this[0]._parseJsonNodes(el.children)
  164. if (!$.isArray(nodeVal)) nodeVal = $.trim(nodeVal)
  165. if (nodeVal.length) opts[item] = ($.inArray(item, boolChkArr) > -1) ? SetBoolean(nodeVal) : nodeVal
  166. }
  167. nodes.push(new Node(opts))
  168. })
  169. return nodes
  170. }
  171. , _parseXmlNodes: function (doc) {
  172. var nodes = []
  173. , $this = $(this)
  174. , boolChkArr = ["leaf","expanded","checkable","checked"]
  175. $.each(doc, function (ind, el) {
  176. var opts = {}
  177. , $el = $(el)
  178. $.each($el.children(), function (x, i) {
  179. var $i = $(i)
  180. , tagName = $i[0].nodeName
  181. , nodeVal = (tagName !== "children") ? $i.text() : $this[0]._parseXmlNodes($i.children("node"))
  182. if (!$.isArray(nodeVal)) nodeVal = $.trim(nodeVal)
  183. if (nodeVal.length) opts[tagName] = ($.inArray(tagName, boolChkArr) > -1) ? SetBoolean(nodeVal) : nodeVal
  184. })
  185. nodes.push(new Node(opts))
  186. })
  187. return nodes
  188. }
  189. , getparentage: function () {
  190. return this.parentage
  191. }
  192. , node: function (el) {
  193. el = el || $(this)
  194. var node = $.extend(true, {}, (el[0] === $(this)[0]) ? $(this.$parent).data() : el.data())
  195. node.branch = this.$element
  196. node.parentage = this.parentage
  197. node.el = (el[0] === $(this)[0]) ? this.$parent : el
  198. delete node.parent
  199. return node
  200. }
  201. }
  202. var Node = function (options) {
  203. $.extend(true, this, {
  204. text: undefined,
  205. leaf: false,
  206. value: undefined,
  207. expanded: false,
  208. cls: undefined,
  209. id: undefined,
  210. href: undefined,
  211. checkable: false,
  212. checked: false,
  213. children: []
  214. }, options)
  215. }
  216. var GetParentBranch = function ($this) {
  217. return $this.closest("ul.branch").prev(".tree-toggle")
  218. }
  219. var GetParentage = function ($this) {
  220. var arr = [], tmp
  221. tmp = GetParentBranch($this)
  222. if (tmp.length) {
  223. arr = GetParentage(tmp)
  224. arr.push(tmp.attr("data-value")||tmp.text())
  225. }
  226. return arr
  227. }
  228. /**
  229. * FUNCTION SetBoolean
  230. *
  231. * Takes any value, and returns it's boolean equivalent.
  232. *
  233. * @param value (any)
  234. * @return (boolean)
  235. */
  236. var SetBoolean = function (value) {
  237. value = $.trim(value)
  238. if (typeof value === "undefined" || value === null) return false
  239. if (typeof value === "string" && !isNaN(value)) value = parseFloat(value)
  240. if (typeof value === "string") {
  241. switch (value.toLowerCase()) {
  242. case "true":
  243. case "yes":
  244. return true
  245. case "false":
  246. case "no":
  247. return false
  248. }
  249. }
  250. return Boolean(value)
  251. }
  252. /* COLLAPSIBLE PLUGIN DEFINITION
  253. * ============================== */
  254. $.fn.tree = function (option) {
  255. return this.each(function () {
  256. var $this = $(this)
  257. , data = $this.data("tree")
  258. , options = typeof option == "object" && option
  259. if (!data) $this.data("tree", (data = new Tree(this, options)))
  260. if (typeof option == "string") data[option]()
  261. })
  262. }
  263. $.fn.tree.defaults = {
  264. toggle: true
  265. }
  266. $.fn.tree.Constructor = Tree
  267. /* COLLAPSIBLE DATA-API
  268. * ==================== */
  269. $(function () {
  270. $("body").on("click.tree.data-api", "[data-toggle=branch]", function (e) {
  271. e.preventDefault()
  272. var $this = $(this)
  273. , target = $this.next(".branch")
  274. , href = $this.attr("href")
  275. , option = $(target).data("tree") ? "toggle" : $this.data()
  276. href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
  277. if (!target.length) {
  278. target = $('<ul>').addClass('branch').append("<li>" + loading + "</li>").insertAfter($this)
  279. }
  280. option.parent = $this
  281. option.href = (href !== "#") ? href : undefined
  282. $(target).tree(option)
  283. return false
  284. })
  285. $("body").on("click.tree.data-api", "[data-role=leaf]", function (e) {
  286. var $this = $(this)
  287. , branch = $this.closest(".branch")
  288. // If not initialized, then create it
  289. if (!$(branch).data("tree")) {
  290. var $target = $(branch)
  291. , branchlink = $target.prev("[data-toggle=branch]")
  292. , branchdata = branchlink.data()
  293. , href = branchlink.attr("href")
  294. href.replace(/.*(?=#[^\s]+$)/, '')
  295. $target.tree($.extend({}, branchdata, {
  296. "toggle": false,
  297. "parent": branchlink,
  298. "href": (href !== "#") ? href : undefined
  299. }))
  300. }
  301. e = $.Event("nodeselect", {
  302. node: $(branch).data("tree").node($this)
  303. })
  304. $this.trigger(e)
  305. })
  306. })
  307. }(window.jQuery);