/*global XSLTProcessor: false, ActiveXObject: false, console : false */ /* File: Helper functions This file includes some functions that enable CSS manipulations, contextmenus, XSLT transformations and drag'n'drop. All of those work independently of jstree. */ /* Variable: $.vakata *object* Holds all helper objects. */ (function ($) { $.vakata = {}; })(jQuery); /* Group: Miscellaneous Various small snippets. */ /* Function: $().vakata_reverse Makes it possible to apply the standard array reverse function to a jQuery collection. Input: >
1
2
3
> $("div").vakata_reverse().each(function () { document.write(this.innerHTML); }); Output: >321 */ (function ($) { $.fn.vakata_reverse = [].reverse; })(jQuery); (function ($) { jQuery.expr[':'].vakata_icontains = function(a,i,m){ return (a.textContent || a.innerText || "").toLowerCase().indexOf(m[3].toLowerCase())>=0; }; })(jQuery); /* Function: $.vakata.array_remove Makes it possible to remove an item (or a group of items) form an array. http://ejohn.org/blog/javascript-array-remove/ Input: > $.vakata.array_remove(['a', 'b', 'c'], 1); Output: >['a', 'c'] */ (function ($) { $.vakata.array_remove = function(array, from, to) { var rest = array.slice((to || from) + 1 || array.length); array.length = from < 0 ? array.length + from : from; array.push.apply(array, rest); return array; }; })(jQuery); /* Function: $.vakata.array_unique Returns only the unique items from an array. Input: > $.vakata.array_unique(['c','a','a','b','c','b']); Output: >['a', 'b', 'c'] */ (function ($) { $.vakata.array_unique = function(array) { var a = [], i, j, l; for(i = 0, l = array.length; i < l; i++) { for(j = 0; j <= i; j++) { if(array[i] === array[j]) { break; } } if(j === i) { a.push(array[i]); } } return a; }; })(jQuery); /* Function: $.vakata.attributes Collects all attributes from a DOM node. */ (function ($) { $.vakata.attributes = function(node, with_values) { node = $(node)[0]; var attr = with_values ? {} : []; $.each(node.attributes, function (i, v) { if($.inArray(v.nodeName.toLowerCase(),['style','contenteditable','hasfocus','tabindex']) !== -1) { return; } if(v.nodeValue !== null && $.trim(v.nodeValue) !== '') { if(with_values) { attr[v.nodeName] = v.nodeValue; } else { attr.push(v.nodeName); } } }); return attr; }; })(jQuery); /* Function: $.vakata.get_scrollbar_width Gets the width of the scrollbar */ (function ($) { var sb; $.vakata.get_scrollbar_width = function () { var e1, e2; if(!sb) { if(/msie/.test(navigator.userAgent.toLowerCase())) { e1 = $('').css({ position: 'absolute', top: -1000, left: 0 }).appendTo('body'); e2 = $('').css({ position: 'absolute', top: -1000, left: 0 }).appendTo('body'); sb = e1.width() - e2.width(); e1.add(e2).remove(); } else { e1 = $('
').css({ width: 100, height: 100, overflow: 'auto', position: 'absolute', top: -1000, left: 0 }) .prependTo('body').append('
').find('div').css({ width: '100%', height: 200 }); sb = 100 - e1.width(); e1.parent().remove(); } } return sb; }; })(jQuery); /* Group: CSS Functions needed to manipulate stylesheets (add, remove, change) */ (function ($) { /* Variable: $.vakata.css *object* holds all CSS related functions */ $.vakata.css = { /* Function: $.vakata.css.get_css Retrieves or deletes a specific rule. Parameters: rule_name - *string* the rule to search for (any CSS rule) delete_flag - *boolean* whether you want to delete or simply retrieve a reference to the rule sheet - the sheet to search in (do not specify this to search in all sheets) Returns either: a reference to the rule - if it was found and the delete flag was not set true - if the delete flag was set and the rule was successfully removed false - if the rule could not be found See also: <$.vakata.css.remove_css> */ get_css : function(rule_name, delete_flag, sheet) { rule_name = rule_name.toLowerCase(); var css_rules = sheet.cssRules || sheet.rules, j = 0; do { if(css_rules.length && j > css_rules.length + 5) { return false; } if(css_rules[j].selectorText && css_rules[j].selectorText.toLowerCase() === rule_name) { if(delete_flag === true) { if(sheet.removeRule) { sheet.removeRule(j); } if(sheet.deleteRule) { sheet.deleteRule(j); } return true; } else { return css_rules[j]; } } } while (css_rules[++j]); return false; }, /* Function: $.vakata.css.add_css Adds a rule. Parameters: rule_name - *string* the rule to add sheet - a reference to the sheet to add to Returns either: a reference to the rule - if the rule was added false - if the rule could not be added, or if such a rule already exists */ add_css : function(rule_name, sheet) { if($.jstree.css.get_css(rule_name, false, sheet)) { return false; } if(sheet.insertRule) { sheet.insertRule(rule_name + ' { }', 0); } else { sheet.addRule(rule_name, null, 0); } return $.vakata.css.get_css(rule_name); }, /* Function: $.vakata.css.remove_css Removes a rule, this functions is a shortcut to <$.vakata.css.get_css> with the delete flag set to true. Parameters: rule_name - *string* the rule to remove sheet - the sheet to remove from (you can omit this and all sheets will be searched) Returns either: true - if rule was removed false - if the rule could not be removed See also: <$.vakata.css.get_css> */ remove_css : function(rule_name, sheet) { return $.vakata.css.get_css(rule_name, true, sheet); }, /* Function: $.vakata.css.add_sheet Adds a whole stylesheet or appends to an existing stylesheet. Parameters: options - *object*: options.url - location of the stylesheet - when this is supplied _options.str_ and _options.title_ should not be set and a new LINK element is always created options.str - text content of the stylesheet - when this is supplied _options.url_ is not used. A STYLE element is used. options.title - the ID of the added stylesheet (if you pass `foo` the ID will be `foo-stylesheet`), when the stylesheet exists the content is appended and no new stylesheet is created. Returns: a reference to the stylesheet */ add_sheet : function(opts) { var tmp = false, is_new = true; if(opts.str) { if(opts.title) { tmp = $("style[id='" + opts.title + "-stylesheet']")[0]; } if(tmp) { is_new = false; } else { tmp = document.createElement("style"); tmp.setAttribute('type',"text/css"); if(opts.title) { tmp.setAttribute("id", opts.title + "-stylesheet"); } } if(tmp.styleSheet) { if(is_new) { document.getElementsByTagName("head")[0].appendChild(tmp); tmp.styleSheet.cssText = opts.str; } else { tmp.styleSheet.cssText = tmp.styleSheet.cssText + " " + opts.str; } } else { tmp.appendChild(document.createTextNode(opts.str)); document.getElementsByTagName("head")[0].appendChild(tmp); } return tmp.sheet || tmp.styleSheet; } if(opts.url) { if(document.createStyleSheet) { try { tmp = document.createStyleSheet(opts.url); } catch (e) { } } else { tmp = document.createElement('link'); tmp.rel = 'stylesheet'; tmp.type = 'text/css'; tmp.media = "all"; tmp.href = opts.url; document.getElementsByTagName("head")[0].appendChild(tmp); return tmp.styleSheet; } } } }; })(jQuery); /* Group: Drag'n'drop Functions needed to drag'n'drop elements */ (function ($) { // private variable var vakata_dnd = { element : false, is_down : false, is_drag : false, helper : false, helper_w: 0, data : false, init_x : 0, init_y : 0, scroll_l: 0, scroll_t: 0, scroll_e: false, scroll_i: false }; /* Variable: $.vakata.dnd *object* holds all DND related functions */ $.vakata.dnd = { /* Variable: $.vakata.dnd.settings *object* holds the global settings object for DND. You can easily modify any of the settings. >// modification example >$.vakata.dnd.settings.threshold = 10; */ settings : { /* Variable: $.vakata.dnd.settings.scroll_speed *integer* how fast (pixel count for each step) should a scrollable parent scroll when dragging near the edge. Default is _10_. */ scroll_speed : 10, /* Variable: $.vakata.dnd.settings.scroll_proximity *integer* number of pixels from the edge of a scrollable parent below which the parent will start scrolling. Default is _20_. */ scroll_proximity : 20, /* Variable: $.vakata.dnd.settings.helper_left *integer* number of pixels left of the cursor to move the drag-helper to. Default is _5_; */ helper_left : 5, /* Variable: $.vakata.dnd.settings.helper_top *integer* number of pixels below the cursor to move the drag-helper to. Default is _10_. */ helper_top : 10, /* Variable: $.vakata.dnd.settings.threshold *integer* amount of pixels required to move before the drag is started. Default is _5_. */ threshold : 5 }, /* Function: $.vakata.dnd._trigger Used internally to trigger all necessary events. */ _trigger : function (event_name, e) { var data = $.vakata.dnd._get(); data.event = e; $(document).triggerHandler("dnd_" + event_name + ".vakata", data); }, /* Function: $.vakata.dnd._get Used internally to get all items for the drag event. Can be used by foreign code too. */ _get : function () { return { "data" : vakata_dnd.data, "element" : vakata_dnd.element, "helper" : vakata_dnd.helper }; }, /* Function: $.vakata.dnd._clean Used internally to cleanup after a drop, so that all variables are nulled and ready for the next drag. */ _clean : function () { if(vakata_dnd.helper) { vakata_dnd.helper.remove(); } if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; } vakata_dnd = { element : false, is_down : false, is_drag : false, helper : false, helper_w: 0, data : false, init_x : 0, init_y : 0, scroll_l: 0, scroll_t: 0, scroll_e: false, scroll_i: false }; $(document).unbind("mousemove", $.vakata.dnd.drag); $(document).unbind("mouseup", $.vakata.dnd.stop); }, /* Function: $.vakata.dnd._scroll Used internally to scroll hovered elements. Triggers: Event: dnd_scroll Fires when a container is scrolled due to dragging near its edge. Triggered on the document, the event is fired in the *vakata* namespace. Parameters: data.event - the scrolled element data.data - the data you supplied when calling <$.vakata.dnd.start> data.element - the origin element data.helper - the jquery extended drag-helper node (or false if it is not used) Example: >$(document).bind("dnd_start.vakata", function (e, data) { > // do something >}); */ _scroll : function (init_only) { if(!vakata_dnd.scroll_e || (!vakata_dnd.scroll_l && !vakata_dnd.scroll_t)) { if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; } return false; } if(!vakata_dnd.scroll_i) { vakata_dnd.scroll_i = setInterval($.vakata.dnd._scroll, 100); return false; } if(init_only === true) { return false; } var i = vakata_dnd.scroll_e.scrollTop(), j = vakata_dnd.scroll_e.scrollLeft(); vakata_dnd.scroll_e.scrollTop(i + vakata_dnd.scroll_t * $.vakata.dnd.settings.scroll_speed); vakata_dnd.scroll_e.scrollLeft(j + vakata_dnd.scroll_l * $.vakata.dnd.settings.scroll_speed); if(i !== vakata_dnd.scroll_e.scrollTop() || j !== vakata_dnd.scroll_e.scrollLeft()) { $.vakata.dnd._trigger("scroll", vakata_dnd.scroll_e); } }, /* Function: $.vakata.dnd.start Use this function to start a drag (usually with the mousedown event) Parameters: event - *event* the event which started the drag, when used with the mousedown event text selection is prevented data - *mixed* some custom data you want to bind with that particular drag - you will receive this in all events html - *mixed* the text for the drag-helper as a *string*, if set to _false_ no helper is shown Returns: false Example: >$("span").bind("mousedown", function (e) { > return $.vakata.dnd.start(e, {}, "Dragging"); >}); */ start : function (e, data, html) { if(vakata_dnd.is_drag) { $.vakata.dnd.stop({}); } try { e.currentTarget.unselectable = "on"; e.currentTarget.onselectstart = function() { return false; }; if(e.currentTarget.style) { e.currentTarget.style.MozUserSelect = "none"; } } catch(err) { } vakata_dnd.init_x = e.pageX; vakata_dnd.init_y = e.pageY; vakata_dnd.data = data; vakata_dnd.is_down = true; vakata_dnd.element = e.currentTarget; if(html !== false) { vakata_dnd.helper = $("
").html(html).css({ "display" : "block", "margin" : "0", "padding" : "0", "position" : "absolute", "top" : "-2000px", "lineHeight" : "16px", "zIndex" : "10000" }); } $(document).bind("mousemove", $.vakata.dnd.drag); $(document).bind("mouseup", $.vakata.dnd.stop); return false; }, /* Function: $.vakata.dnd.drag Used internally to process the mousemove event after <$.vakata.dnd.start> is called. Parameters: event - *event* the mousemove event Triggers: , */ drag : function (e) { if(!vakata_dnd.is_down) { return; } if(!vakata_dnd.is_drag) { if( Math.abs(e.pageX - vakata_dnd.init_x) > $.vakata.dnd.settings.threshold || Math.abs(e.pageY - vakata_dnd.init_y) > $.vakata.dnd.settings.threshold ) { if(vakata_dnd.helper) { vakata_dnd.helper.appendTo("body"); vakata_dnd.helper_w = vakata_dnd.helper.outerWidth(); } vakata_dnd.is_drag = true; /* Event: dnd_start Marks the start of the drag. Triggered on the document after a drag is initiated using <$.vakata.dnd.start> and the user has moved more than <$.vakata.dnd.settings.threshold> pixels, the event is fired in the *vakata* namespace. Parameters: data.event - the mousemove event data.data - the data you supplied when calling <$.vakata.dnd.start> data.element - the origin element data.helper - the jquery extended drag-helper node (or false if it is not used) Example: >$(document).bind("dnd_start.vakata", function (e, data) { > // do something >}); */ $.vakata.dnd._trigger("start", e); } else { return; } } var d = false, w = false, dh = false, wh = false, dw = false, ww = false, dt = false, dl = false, ht = false, hl = false; vakata_dnd.scroll_t = 0; vakata_dnd.scroll_l = 0; vakata_dnd.scroll_e = false; var p = $(e.target) .parentsUntil("body").addBack().vakata_reverse() .filter(function () { return (/^auto|scroll$/).test($(this).css("overflow")) && (this.scrollHeight > this.offsetHeight || this.scrollWidth > this.offsetWidth); }) .each(function () { var t = $(this), o = t.offset(); if(this.scrollHeight > this.offsetHeight) { if(o.top + t.height() - e.pageY < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; } if(e.pageY - o.top < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; } } if(this.scrollWidth > this.offsetWidth) { if(o.left + t.width() - e.pageX < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; } if(e.pageX - o.left < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; } } if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) { vakata_dnd.scroll_e = $(this); return false; } }); if(!vakata_dnd.scroll_e) { d = $(document); w = $(window); dh = d.height(); wh = w.height(); dw = d.width(); ww = w.width(); dt = d.scrollTop(); dl = d.scrollLeft(); if(dh > wh && e.pageY - dt < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; } if(dh > wh && wh - (e.pageY - dt) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; } if(dw > ww && e.pageX - dl < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; } if(dw > ww && ww - (e.pageX - dl) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; } if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) { vakata_dnd.scroll_e = d; } } if(vakata_dnd.scroll_e) { $.vakata.dnd._scroll(true); } if(vakata_dnd.helper) { ht = parseInt(e.pageY + $.vakata.dnd.settings.helper_top, 10); hl = parseInt(e.pageX + $.vakata.dnd.settings.helper_left, 10); if(dh && ht + 25 > dh) { ht = dh - 50; } if(dw && hl + vakata_dnd.helper_w > dw) { hl = dw - (vakata_dnd.helper_w + 2); } vakata_dnd.helper.css({ left : hl + "px", top : ht + "px" }); } /* Event: dnd_move Triggered multiple times while dragging. This event is triggered on the document after the event when the user moves the mouse, the event is fired in the *vakata* namespace. Parameters: data.event - the mousemove event data.data - the data you supplied when calling <$.vakata.dnd.start> data.element - the origin element data.helper - the jquery extended drag-helper node (or false if it is not used) Example: >$(document).bind("dnd_move.vakata", function (e, data) { > // do something >}); */ $.vakata.dnd._trigger("move", e); }, /* Function: $.vakata.dnd.stop Used internally to process the mouseup event (drop) after <$.vakata.dnd.start> is called. Parameters: event - *event* the mouseup event Triggers: */ stop : function (e) { /* Event: dnd_stop Marks the end of the drag. This event is triggered on the document after (and possibly ) when a drop (mouseup) occurs or when the drag is programatically terminated, the event is fired in the *vakata* namespace. Parameters: data.event - the mouseup event (or _null_ if stopped programatically using <$.vakata.dnd.stop>()) data.data - the data you supplied when calling <$.vakata.dnd.start> data.element - the origin element data.helper - the jquery extended drag-helper node (or false if it is not used) Example: >$(document).bind("dnd_stop.vakata", function (e, data) { > // do something >}); */ if(vakata_dnd.is_drag) { $.vakata.dnd._trigger("stop", e); } $.vakata.dnd._clean(); } }; })(jQuery); /* Group: XSLT A function used to do XSLT transformations. */ (function ($) { /* Function: $.vakata.xslt This functions transforms a XML string using a XSL string. The result is passed to a callback function. Parameters: xml - *string* the source xml string xsl - *string* the xsl string Returns: the transformed result (or _false_ on failure) Example: >// simple >$.vakata.xslt("", "", function (res) { $("#some-container").append(res); }); >// with scope >$.vakata.xslt("", "", $.proxy(function (res) { > this.some_process(res); >}, some_object); */ $.vakata.xslt = function (xml, xsl) { var r = false, p, q, s; // IE9 if(r === false && window.ActiveXObject) { try { r = new ActiveXObject("Msxml2.XSLTemplate"); q = new ActiveXObject("Msxml2.DOMDocument"); q.loadXML(xml); s = new ActiveXObject("Msxml2.FreeThreadedDOMDocument"); s.loadXML(xsl); r.stylesheet = s; p = r.createProcessor(); p.input = q; p.transform(); r = p.output; } catch (e) { } } xml = $.parseXML(xml); xsl = $.parseXML(xsl); // FF, Chrome if(r === false && typeof (XSLTProcessor) !== "undefined") { p = new XSLTProcessor(); p.importStylesheet(xsl); r = p.transformToFragment(xml, document); r = $('
').append(r).html(); } // OLD IE if(r === false && typeof (xml.transformNode) !== "undefined") { r = xml.transformNode(xsl); } return r; }; })(jQuery); /* Group: Hotkeys Copy of the John Resig's fork of http://github.com/tzuryby/hotkeys for consistency */ if(typeof jQuery.hotkeys === "undefined") { (function ($) { $.vakata_hotkeys = { version: "0.8", specialKeys: { 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause", 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home", 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del", 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/", 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8", 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta" }, shiftNums: { "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<", ".": ">", "/": "?", "\\": "|" } }; function keyHandler( handleObj ) { // Only care when a possible input has been specified if ( typeof handleObj.data !== "string" ) { return; } var origHandler = handleObj.handler, keys = handleObj.data.toLowerCase().split(" "); handleObj.handler = function( event ) { // Don't fire in text-accepting inputs that we didn't directly bind to if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) || event.target.type === "text") ) { return; } // Keypress represents characters, not special keys var special = event.type !== "keypress" && jQuery.vakata_hotkeys.specialKeys[ event.which ], character = String.fromCharCode( event.which ).toLowerCase(), key, modif = "", possible = {}; // check combinations (alt|ctrl|shift+anything) if ( event.altKey && special !== "alt" ) { modif += "alt+"; } if ( event.ctrlKey && special !== "ctrl" ) { modif += "ctrl+"; } // TODO: Need to make sure this works consistently across platforms if ( event.metaKey && !event.ctrlKey && special !== "meta" ) { modif += "meta+"; } if ( event.shiftKey && special !== "shift" ) { modif += "shift+"; } if ( special ) { possible[ modif + special ] = true; } else { possible[ modif + character ] = true; possible[ modif + jQuery.vakata_hotkeys.shiftNums[ character ] ] = true; // "$" can be triggered as "Shift+4" or "Shift+$" or just "$" if ( modif === "shift+" ) { possible[ jQuery.vakata_hotkeys.shiftNums[ character ] ] = true; } } for ( var i = 0, l = keys.length; i < l; i++ ) { if ( possible[ keys[i] ] ) { return origHandler.apply( this, arguments ); } } }; } jQuery.each([ "keydown", "keyup", "keypress" ], function() { jQuery.event.special[ this ] = { add: keyHandler }; }); })(jQuery); } /* Group: Context menu Functions needed to show a custom context menu. */ (function ($) { var right_to_left = false, vakata_context = { element : false, reference : false, position_x : 0, position_y : 0, items : [], html : "", is_visible : false }; /* Variable: $.vakata.context *object* holds all context menu related functions and variables. */ $.vakata.context = { /* Variable: $.vakata.context.settings *object* holds the global settings object for context menus. You can easily modify any of the settings. >// modification example >$.vakata.context.settings.icons = false; */ settings : { /* Variable: $.vakata.context.settings.hide_onmouseleave *integer* the amount of milliseconds to wait before hiding the menu after mouseleave. If set to _0_ the menu won't hide on mouseleave. Default is _0_. */ hide_onmouseleave : 0, /* Variable: $.vakata.context.settings.icons *boolean* whether to show icons or not. Default is _true_. */ icons : true }, /* Function: $.vakata.context._trigger Used internally to trigger all necessary events. */ _trigger : function (event_name) { $(document).triggerHandler("context_" + event_name + ".vakata", { "reference" : vakata_context.reference, "element" : vakata_context.element, "position" : { "x" : vakata_context.position_x, "y" : vakata_context.position_y } }); }, /* Function: $.vakata.context._execute Used internally to execute the action (if any) associated with an item. Parameters: i - the item's internal index */ _execute : function (i) { i = vakata_context.items[i]; return i && !i._disabled && i.action ? i.action.call(null, { "item" : i, "reference" : vakata_context.reference, "element" : vakata_context.element, "position" : { "x" : vakata_context.position_x, "y" : vakata_context.position_y } }) : false; }, /* Function: $.vakata.context._parse Used internally to parse a contextmenu description object to an HTML string. Parameters: o - *object* the contextmenu description object is_callback - *boolean* used internally to indicate a recursive call Triggers: */ _parse : function (o, is_callback) { if(!o) { return false; } if(!is_callback) { vakata_context.html = ""; vakata_context.items = []; } var str = "", sep = false, tmp; if(is_callback) { str += ""; } /* Event: context_parse Triggered when the context menu is parsed but not yet shown. This event is triggered on the document in the *vakata* namespace. Parameters: reference - the DOM node used when <$.vakata.context.show> was called element - the DOM node of the context menu (not yet populated and shown) position - an object consisting of _x_ and _y_ keys, represinting the position of the menu (not yet shown) Example: >$(document).bind("context_parse.vakata", function (e, data) { > // do something >}); */ if(!is_callback) { vakata_context.html = str; $.vakata.context._trigger("parse"); } return str.length > 10 ? str : false; }, /* Function: $.vakata.context._show_submenu Used internally to show a submenu */ _show_submenu : function (o) { o = $(o); if(!o.length || !o.children("ul").length) { return; } var e = o.children("ul"), x = o.offset().left + o.outerWidth(), y = o.offset().top, w = e.width(), h = e.height(), dw = $(window).width() + $(window).scrollLeft(), dh = $(window).height() + $(window).scrollTop(); // може да се спести е една проверка - дали няма някой от класовете вече нагоре if(right_to_left) { o[x - (w + 10 + o.outerWidth()) < 0 ? "addClass" : "removeClass"]("vakata-context-left"); } else { o[x + w + 10 > dw ? "addClass" : "removeClass"]("vakata-context-right"); } if(y + h + 10 > dh) { e.css("bottom","-1px"); } e.show(); }, /* Function: $.vakata.context.show Shows the context menu. Please note that at least one of _reference_ or _position_ should be specified. Parameters: reference - *jquery* associate the menu with a DOM element (optional) position - *object* should contain _x_ and _y_ properties, those are the coordinates to show the menu at (optional data - *object* the contextmenu description object. It should consist of keys, each key should be a . If not specified the function will search for $(reference).data('vakata_contextmenu') and use that. Triggers: Example: >$(document).bind("contextmenu", function (e) { > e.preventDefault(); > $.vakata.context.show(false, { x: e.pageX, y:e.pageY }, { > "create" : { > // only specify what you need > "separator_after" : true, > "label" : "Create", > "action" : function (data) { alert("Create"); } > }, > "rename" : { > "label" : "Rename", > "icon" : "./some-icon.png", > "action" : function (data) { alert("Rename on " + data.reference); } > }, > "edit" : { > "label" : "Edit", > // Clicking this won't hide the menu, the same can be achieved with: > // "action" : function () { return false; } > "submenu" : { > "copy" : { "label" : "Copy", "action" : function () { } }, > "cut" : { "label" : "Cut", "action" : function () { } }, > "paste" : { "label" : "Paste", "_disabled" : true, "action" : function () { } } > } > }, > "delete" : { > "separator_before" : true, > "label" : "Delete", > "action" : function (data) { alert("Delete"); } > } > }); >}); Variable: context_menu_item *object* Used to construct a context menu entry, this structure will always be a part of an object. separator_before - *boolean* should there be a separator before the item. Default is _false_. separator_after - *boolean* should there be a separator after the item. Default is _false_. icon - *string* if supplied this string is used for an icon, if it contains _/_ it is treated as file, otherwise it is applied as a class on an INS object. label - *string* the text for this item submenu - *object* if supplied this object is used to build a submenu. It should consist of keys, each of which is a . _class - *string* if supplied this class is applied to the LI node. _disabled - *boolean* is this item disabled. action - *functon* if supplied it will be executed when this item is clicked / activated. If not supplied or the function returns _false_ the contextmenu won't be hidden after execution. To force a context use _$.proxy_. In the function you will receive a single argument which is an object, consisting of four keys: _item_ (the object), _reference_ (the DOM node used when <$.vakata.context.show> was called), _element_ (the DOM node of the context menu), _position_ (an object consisting of _x_ and _y_ keys, represinting the current position of the menu) See also: <$.vakata.context.show> */ show : function (reference, position, data) { if(vakata_context.element && vakata_context.element.length) { vakata_context.element.width(''); } switch(!0) { case (!position && !reference): return false; case (!!position && !!reference): vakata_context.reference = reference; vakata_context.position_x = position.x; vakata_context.position_y = position.y; break; case (!position && !!reference): vakata_context.reference = reference; var o = reference.offset(); vakata_context.position_x = o.left + reference.outerHeight(); vakata_context.position_y = o.top; break; case (!!position && !reference): vakata_context.position_x = position.x; vakata_context.position_y = position.y; break; } if(!!reference && !data && $(reference).data('vakata_contextmenu')) { data = $(reference).data('vakata_contextmenu'); } if($.vakata.context._parse(data)) { vakata_context.element.html(vakata_context.html); } if(vakata_context.items.length) { var e = vakata_context.element, x = vakata_context.position_x, y = vakata_context.position_y, w = e.width(), h = e.height(), dw = $(window).width() + $(window).scrollLeft(), dh = $(window).height() + $(window).scrollTop(); if(right_to_left) { x -= e.outerWidth(); if(x < $(window).scrollLeft() + 20) { x = $(window).scrollLeft() + 20; } } if(x + w + 20 > dw) { x = dw - (w + 20); } if(y + h + 20 > dh) { y = dh - (h + 20); } vakata_context.element .css({ "left" : x, "top" : y }) .show() .width(vakata_context.element.outerWidth()); // for ie6 vakata_context.is_visible = true; /* Event: context_show Triggered when the context menu is shown. This event is triggered on the document in the *vakata* namespace. Parameters: reference - the DOM node used when <$.vakata.context.show> was called element - the DOM node of the context menu position - an object consisting of _x_ and _y_ keys, represinting the position of the menu Example: >$(document).bind("context_show.vakata", function (e, data) { > // do something >}); */ $.vakata.context._trigger("show"); } }, /* Function: $.vakata.context.hide Used internally to hide the contextmenu after a click, or on mouseleave, etc. Triggers: */ hide : function () { if(vakata_context.is_visible) { vakata_context.element.hide().find("ul").hide(); vakata_context.is_visible = false; /* Event: context_hide Triggered when the context menu is hidden. This event is triggered on the document in the *vakata* namespace. Parameters: reference - the DOM node used when <$.vakata.context.show> was called element - the DOM node of the context menu position - an object consisting of _x_ and _y_ keys, represinting the position of the menu Example: >$(document).bind("context_hide.vakata", function (e, data) { > // do something >}); */ $.vakata.context._trigger("hide"); } } }; $(function () { right_to_left = $("body").css("direction") === "rtl"; var to = false, css_string = '' + '.vakata-context { display:none; _width:1px; } ' + '.vakata-context, ' + '.vakata-context ul { margin:0; padding:2px; position:absolute; background:#f5f5f5; border:1px solid #979797; ' + ' -moz-box-shadow:5px 5px 4px -4px #666666; -webkit-box-shadow:2px 2px 2px #999999; box-shadow:2px 2px 2px #999999; }' + '.vakata-context ul { list-style:none; left:100%; margin-top:-2.7em; margin-left:-4px; } ' + '.vakata-context li.vakata-context-right ul { left:auto; right:100%; margin-left:auto; margin-right:-4px; } ' + '.vakata-context li { list-style:none; display:inline; }' + '.vakata-context li a { display:block; padding:0 2em 0 2em; text-decoration:none; width:auto; color:black; white-space:nowrap; line-height:2.4em; ' + ' -moz-text-shadow:1px 1px 0px white; -webkit-text-shadow:1px 1px 0px white; text-shadow:1px 1px 0px white; ' + ' -moz-border-radius:1px; -webkit-border-radius:1px; border-radius:1px; }' + '.vakata-context li a:hover { position:relative; background-color:#e8eff7; ' + ' -moz-box-shadow:0px 0px 2px #0a6aa1; -webkit-box-shadow:0px 0px 2px #0a6aa1; box-shadow:0px 0px 2px #0a6aa1; }' + '.vakata-context li.vakata-context-hover > a { position:relative; background-color:#e8eff7; ' + ' -moz-box-shadow:0px 0px 2px #0a6aa1; -webkit-box-shadow:0px 0px 2px #0a6aa1; box-shadow:0px 0px 2px #0a6aa1; }' + '.vakata-context li a.vakata-context-parent { background-image:url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAIORI4JlrqN1oMSnmmZDQUAOw=="); background-position:right center; background-repeat:no-repeat; } ' + '.vakata-context li.vakata-context-separator a, ' + '.vakata-context li.vakata-context-separator a:hover { background:white; border:0; border-top:1px solid #e2e3e3; height:1px; min-height:1px; max-height:1px; padding:0; margin:0 0 0 2.4em; border-left:1px solid #e0e0e0; _overflow:hidden; ' + ' -moz-text-shadow:0 0 0 transparent; -webkit-text-shadow:0 0 0 transparent; text-shadow:0 0 0 transparent; ' + ' -moz-box-shadow:0 0 0 transparent; -webkit-box-shadow:0 0 0 transparent; box-shadow:0 0 0 transparent; ' + ' -moz-border-radius:0; -webkit-border-radius:0; border-radius:0; }' + '.vakata-context li.vakata-contextmenu-disabled a, .vakata-context li.vakata-contextmenu-disabled a:hover { color:silver; background-color:transparent; border:0; box-shadow:0 0 0; }' + '' + '.vakata-context li a ins { text-decoration:none; display:inline-block; width:2.4em; height:2.4em; background:transparent; margin:0 0 0 -2em; } ' + '.vakata-context li a span { display:inline-block; width:1px; height:2.4em; background:white; margin:0 0.5em 0 0; border-left:1px solid #e2e3e3; _overflow:hidden; } ' + '' + '.vakata-context-rtl ul { left:auto; right:100%; margin-left:auto; margin-right:-4px; } ' + '.vakata-context-rtl li a.vakata-context-parent { background-image:url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAINjI+AC7rWHIsPtmoxLAA7"); background-position:left center; background-repeat:no-repeat; } ' + '.vakata-context-rtl li.vakata-context-separator a { margin:0 2.4em 0 0; border-left:0; border-right:1px solid #e2e3e3;} ' + '.vakata-context-rtl li.vakata-context-left ul { right:auto; left:100%; margin-left:-4px; margin-right:auto; } ' + '.vakata-context-rtl li a ins { margin:0 -2em 0 0; } ' + '.vakata-context-rtl li a span { margin:0 0 0 0.5em; border-left-color:white; background:#e2e3e3; } ' + ''; $.vakata.css.add_sheet({ str : css_string, title : "vakata-context" }); vakata_context.element = $("
    "); vakata_context.element .delegate("li", "mouseenter", function (e) { e.stopImmediatePropagation(); if($.contains(this, e.relatedTarget)) { // премахнато заради delegate mouseleave по-долу // $(this).find(".vakata-context-hover").removeClass("vakata-context-hover"); return; } if(to) { clearTimeout(to); } vakata_context.element.find(".vakata-context-hover").removeClass("vakata-context-hover").end(); $(this) .siblings().find("ul").hide().end().end() .parentsUntil(".vakata-context", "li").addBack().addClass("vakata-context-hover"); $.vakata.context._show_submenu(this); }) // тестово - дали не натоварва? .delegate("li", "mouseleave", function (e) { if($.contains(this, e.relatedTarget)) { return; } $(this).find(".vakata-context-hover").addBack().removeClass("vakata-context-hover"); }) .bind("mouseleave", function (e) { $(this).find(".vakata-context-hover").removeClass("vakata-context-hover"); if($.vakata.context.settings.hide_onmouseleave) { to = setTimeout( (function (t) { return function () { $.vakata.context.hide(); }; })(this), $.vakata.context.settings.hide_onmouseleave); } }) .delegate("a", "click", function (e) { e.preventDefault(); }) .delegate("a", "mouseup", function (e) { if(!$(this).blur().parent().hasClass("vakata-context-disabled") && $.vakata.context._execute($(this).attr("rel")) !== false) { $.vakata.context.hide(); } }) .appendTo("body"); $(document) .bind("mousedown", function (e) { if(vakata_context.is_visible && !$.contains(vakata_context.element[0], e.target)) { $.vakata.context.hide(); } }) .bind("context_show.vakata", function (e, data) { vakata_context.element.find("li:has(ul)").children("a").addClass("vakata-context-parent"); if(right_to_left) { vakata_context.element.addClass("vakata-context-rtl").css("direction", "rtl"); } // also apply a RTL class? vakata_context.element.find("ul").hide().end(); }); if(typeof $.hotkeys !== "undefined" || typeof $.vakata_hotkeys !== "undefined") { $(document) .bind("keydown", "up", function (e) { if(vakata_context.is_visible) { var o = vakata_context.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").prevAll("li:not(.vakata-context-separator)").first(); if(!o.length) { o = vakata_context.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").last(); } o.addClass("vakata-context-hover"); e.stopImmediatePropagation(); e.preventDefault(); } }) .bind("keydown", "down", function (e) { if(vakata_context.is_visible) { var o = vakata_context.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").nextAll("li:not(.vakata-context-separator)").first(); if(!o.length) { o = vakata_context.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").first(); } o.addClass("vakata-context-hover"); e.stopImmediatePropagation(); e.preventDefault(); } }) .bind("keydown", "right", function (e) { if(vakata_context.is_visible) { vakata_context.element.find(".vakata-context-hover").last().children("ul").show().children("li:not(.vakata-context-separator)").removeClass("vakata-context-hover").first().addClass("vakata-context-hover"); e.stopImmediatePropagation(); e.preventDefault(); } }) .bind("keydown", "left", function (e) { if(vakata_context.is_visible) { vakata_context.element.find(".vakata-context-hover").last().parents("li:eq(0)").find("ul").hide().find(".vakata-context-hover").removeClass("vakata-context-hover"); e.stopImmediatePropagation(); e.preventDefault(); } }) .bind("keydown", "esc", function (e) { $.vakata.context.hide(); e.preventDefault(); }) .bind("keydown", "space", function (e) { vakata_context.element.find(".vakata-context-hover").last().children("a").click(); e.preventDefault(); }); } }); })(jQuery); /* Group: JSON Functions needed to encode/decode JSON. Based on the jQuery JSON Plugin. */ (function ($) { // private function for quoting strings var _quote = function (str) { var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g, meta = { '\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"' :'\\"','\\':'\\\\' }; if(str.match(escapeable)) { return '"' + str.replace(escapeable, function (a) { var c = meta[a]; if(typeof c === 'string') { return c; } c = a.charCodeAt(); return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16); }) + '"'; } return '"' + str + '"'; }; /* Variable: $.vakata.json *object* holds all JSON related functions. */ $.vakata.json = { /* Function: $.vakata.json.encode A function for encoding data in a JSON notated string. Parameters: o - *mixed* the data to be encoded Returns: string - the encoded data */ encode : function (o) { if (o === null) { return "null"; } var tmp = [], i; switch(typeof(o)) { case "undefined": return undefined; case "number": case "boolean": return o + ""; case "string": return _quote(o); case "object": if($.isFunction(o.toJSON)) { return $.vakata.json.encode(o.toJSON()); } if(o.constructor === Date) { return '"' + o.getUTCFullYear() + '-' + String("0" + (o.getUTCMonth() + 1)).slice(-2) + '-' + String("0" + o.getUTCDate()).slice(-2) + 'T' + String("0" + o.getUTCHours()).slice(-2) + ':' + String("0" + o.getUTCMinutes()).slice(-2) + ':' + String("0" + o.getUTCSeconds()).slice(-2) + '.' + String("00" + o.getUTCMilliseconds()).slice(-3) + 'Z"'; } if(o.constructor === Array) { for(i = 0; i < o.length; i++) { tmp.push( $.vakata.json.encode(o[i]) || "null" ); } return "[" + tmp.join(",") + "]"; } $.each(o, function (i, v) { if($.isFunction(v)) { return true; } i = typeof i === "number" ? '"' + i + '"' : _quote(i); v = $.vakata.json.encode(v); tmp.push(i + ":" + v); }); return "{" + tmp.join(", ") + "}"; } }, /* Function: $.vakata.json.decode Exists for consistency and is a simple wrapper for jQuery.parseJSON. Parameters: json - the string to be decoded Returns: Same as jQuery.parseJSON */ decode : function (json) { return $.parseJSON(json); } }; })(jQuery); /* Group: Cookie A copy of the jQuery cookie plugin. */ (function ($, document, undefined) { /* Function: $.vakata.cookie A function for getting and setting cookies. Parameters: Same as the original plugin Returns: string - the encoded data */ var raw = function (s) { return s; }, decoded = function (s) { return decodeURIComponent(s.replace(/\+/g, ' ')); }; var config = $.vakata.cookie = function (key, value, options) { // write if (value !== undefined) { options = $.extend({}, config.defaults, options); if (value === null) { options.expires = -1; } if (typeof options.expires === 'number') { var days = options.expires, t = options.expires = new Date(); t.setDate(t.getDate() + days); } value = config.json ? $.vakata.json.encode(value) : String(value); return (document.cookie = [ encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value), options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE options.path ? '; path=' + options.path : '', options.domain ? '; domain=' + options.domain : '', options.secure ? '; secure' : '' ].join('')); } // read var decode = config.raw ? raw : decoded; var cookies = document.cookie.split('; '); for (var i = 0, l = cookies.length; i < l; i++) { var parts = cookies[i].split('='); if (decode(parts.shift()) === key) { var cookie = decode(parts.join('=')); return config.json ? $.vakata.json.decode(cookie) : cookie; } } return null; }; config.defaults = {}; $.vakata.removeCookie = function (key, options) { if ($.cookie(key) !== null) { $.cookie(key, null, options); return true; } return false; }; })(jQuery, document); /* Group: LocalStorage Functions for dealing with localStorage with fallback to userData or cookies. A slight modification of jstorage. */ (function ($) { var _storage = {}, _storage_service = {jStorage:"{}"}, _storage_elm = null, _storage_size = 0, json_encode = $.vakata.json.encode, json_decode = $.vakata.json.decode, _backend = false, _ttl_timeout = false; function _init() { var localStorageReallyWorks = false; if("localStorage" in window){ try { window.localStorage.setItem('_tmptest', 'tmpval'); localStorageReallyWorks = true; window.localStorage.removeItem('_tmptest'); } catch(BogusQuotaExceededErrorOnIos5) { // Thanks be to iOS5 Private Browsing mode which throws // QUOTA_EXCEEDED_ERRROR DOM Exception 22. } } if(localStorageReallyWorks){ try { if(window.localStorage) { _storage_service = window.localStorage; _backend = "localStorage"; } } catch(E3) {/* Firefox fails when touching localStorage and cookies are disabled */} } else if("globalStorage" in window) { try { if(window.globalStorage) { _storage_service = window.globalStorage[window.location.hostname]; _backend = "globalStorage"; } } catch(E4) {/* Firefox fails when touching localStorage and cookies are disabled */} } else { _storage_elm = document.createElement('link'); if(_storage_elm.addBehavior) { _storage_elm.style.behavior = 'url(#default#userData)'; document.getElementsByTagName('head')[0].appendChild(_storage_elm); try { _storage_elm.load("jStorage"); var data = "{}"; data = _storage_elm.getAttribute("jStorage"); _storage_service.jStorage = data; _backend = "userDataBehavior"; } catch(E5) {} } if( !_backend && ( !!$.vakata.cookie('__vjstorage') || ($.vakata.cookie('__vjstorage', '{}', { 'expires' : 365 }) && $.vakata.cookie('__vjstorage') === '{}') ) ) { _storage_elm = null; _storage_service.jStorage = $.vakata.cookie('__vjstorage'); _backend = "cookie"; } if(!_backend) { _storage_elm = null; return; } } _load_storage(); _handleTTL(); } function _load_storage() { if(_storage_service.jStorage) { try { _storage = json_decode(String(_storage_service.jStorage)); } catch(E6) { _storage_service.jStorage = "{}"; } } else { _storage_service.jStorage = "{}"; } _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0; } function _save() { try { _storage_service.jStorage = json_encode(_storage); if(_backend === 'userDataBehavior') { _storage_elm.setAttribute("jStorage", _storage_service.jStorage); _storage_elm.save("jStorage"); } if(_backend === 'cookie') { $.vakata.cookie('__vjstorage', _storage_service.jStorage, { 'expires' : 365 }); } _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0; } catch(E7) { /* probably cache is full, nothing is saved this way*/ } } function _checkKey(key) { if(!key || (typeof key !== "string" && typeof key !== "number")){ throw new TypeError('Key name must be string or numeric'); } if(key === "__jstorage_meta") { throw new TypeError('Reserved key name'); } return true; } function _handleTTL() { var curtime = +new Date(), i, TTL, nextExpire = Infinity, changed = false; if(_ttl_timeout !== false) { clearTimeout(_ttl_timeout); } if(!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL !== "object"){ return; } TTL = _storage.__jstorage_meta.TTL; for(i in TTL) { if(TTL.hasOwnProperty(i)) { if(TTL[i] <= curtime) { delete TTL[i]; delete _storage[i]; changed = true; } else if(TTL[i] < nextExpire) { nextExpire = TTL[i]; } } } // set next check if(nextExpire !== Infinity) { _ttl_timeout = setTimeout(_handleTTL, nextExpire - curtime); } // save changes if(changed) { _save(); } } /* Variable: $.vakata.storage *object* holds all storage related functions and properties. */ $.vakata.storage = { /* Variable: $.vakata.storage.version *string* the version of jstorage used HEAVILY MODIFIED */ version: "0.3.0", /* Function: $.vakata.storage.set Set a key to a value Parameters: key - the key value - the value Returns: _value_ */ set : function (key, value, ttl) { _checkKey(key); if(typeof value === "object") { value = json_decode(json_encode(value)); } _storage[key] = value; _save(); if(ttl && parseInt(ttl, 10)) { $.vakata.storage.setTTL(key, parseInt(ttl, 10)); } return value; }, /* Function: $.vakata.storage.get Get a value by key. Parameters: key - the key def - the value to return if _key_ is not found Returns: The found value, _def_ if key not found or _null_ if _def_ is not supplied. */ get : function (key, def) { _checkKey(key); if(key in _storage){ return _storage[key]; } return typeof(def) === 'undefined' ? null : def; }, /* Function: $.vakata.storage.del Remove a key. Parameters: key - the key Returns: *boolean* */ del : function (key) { _checkKey(key); if(key in _storage) { delete _storage[key]; if(_storage.__jstorage_meta && typeof _storage.__jstorage_meta.TTL === "object" && key in _storage.__jstorage_meta.TTL) { delete _storage.__jstorage_meta.TTL[key]; } _save(); return true; } return false; }, setTTL: function(key, ttl){ var curtime = +new Date(); _checkKey(key); ttl = Number(ttl) || 0; if(key in _storage){ if(!_storage.__jstorage_meta){ _storage.__jstorage_meta = {}; } if(!_storage.__jstorage_meta.TTL) { _storage.__jstorage_meta.TTL = {}; } if(ttl > 0) { _storage.__jstorage_meta.TTL[key] = curtime + ttl; } else { delete _storage.__jstorage_meta.TTL[key]; } _save(); _handleTTL(); return true; } return false; }, getTTL: function(key){ var curtime = +new Date(), ttl; _checkKey(key); if(key in _storage && _storage.__jstorage_meta.TTL && _storage.__jstorage_meta.TTL[key]) { ttl = _storage.__jstorage_meta.TTL[key] - curtime; return ttl || 0; } return 0; }, /* Function: $.vakata.storage.flush Empty the storage. Returns: _true_ */ flush : function(){ _storage = {}; _save(); // try{ window.localStorage.clear(); } catch(E8) { } return true; }, /* Function: $.vakata.storage.storageObj Get a read only copy of the whole storage. Returns: *object* */ storageObj : function(){ return $.extend(true, {}, _storage); }, /* Function: $.vakata.storage.index Get an array of all the set keys in the storage. Returns: *array* */ index : function(){ var index = [], i; $.each(_storage, function (i, v) { if(i !== "__jstorage_meta") { index.push(i); } }); return index; }, /* Function: $.vakata.storage.storageSize Get the size of all items in the storage in bytes. Returns: *number* */ storageSize : function(){ return _storage_size; }, /* Function: $.vakata.storage.currentBackend Get the current backend used. Returns: *string* */ currentBackend : function(){ return _backend; }, /* Function: $.vakata.storage.storageAvailable See if storage functionality is available. Returns: *boolean* */ storageAvailable : function(){ return !!_backend; } }; _init(); })(jQuery); /* Group: PrettyDate Modifies time elements to a more human readable value. Taken from: https://github.com/zachleat/Humane-Dates/blob/master/src/humane.js */ (function ($) { /* Variable: $.vakata.pretty_date *object* holds all pretty-date related functions and properties. */ $.vakata.pretty_date = { /* Variable: $.vakata.pretty_date.lang *object* the localization to use. */ lang : { ago: 'Ago', from: 'From Now', now: 'Just Now', minute: 'Minute', minutes: 'Minutes', hour: 'Hour', hours: 'Hours', day: 'Day', days: 'Days', week: 'Week', weeks: 'Weeks', month: 'Month', months: 'Months', year: 'Year', years: 'Years' }, /* Function: $.vakata.pretty_date.parse Parses the difference between to dates to a human readable string. Parameters: date - the date to calculate from (а string in this YYYY-MM-DDTHH:MM:SSZ format - UTC) comareTo - the date to compare to (as date), if left empty the current date is used Returns: *mixed* - the formatted string on success or _null_ on error */ parse : function (date, compareTo) { // remove the timezone (always use gmdate on server side) date = new Date(date.replace(/-/g,"/").replace(/[TZ]/g," ").replace(/\+\d\d\:\d\d$/,'').replace(/\+\d\d\d\d$/,'')); compareTo = compareTo || new Date(); var lang = $.vakata.pretty_date.lang, formats = [ [60, lang.now], [3600, lang.minute, lang.minutes, 60], // 60 minutes, 1 minute [86400, lang.hour, lang.hours, 3600], // 24 hours, 1 hour [604800, lang.day, lang.days, 86400], // 7 days, 1 day [2628000, lang.week, lang.weeks, 604800], // ~1 month, 1 week [31536000, lang.month, lang.months, 2628000], // 1 year, ~1 month [Infinity, lang.year, lang.years, 31536000] // Infinity, 1 year ], seconds = (compareTo - date + compareTo.getTimezoneOffset() * 60000) / 1000, normalize = function (val, single) { var margin = 0.1; if(val >= single && val <= single * (1+margin)) { return single; } return val; }, token; if(seconds < 0) { seconds = Math.abs(seconds); token = ' ' + lang.from; } else { token = ' ' + lang.ago; } for(var i = 0, format = formats[0]; formats[i]; format = formats[++i]) { if(seconds < format[0]) { if(i === 0) { return format[1]; } var val = Math.ceil(normalize(seconds, format[3]) / (format[3])); return val + ' ' + (val !== 1 ? format[2] : format[1]) + (i > 0 ? token : ''); } } }, /* Function: $.vakata.pretty_date.init Parses all time elements in the document and keeps reparsing them every few seconds. Parameters: i - the interval for reparsing (in milliseconds). Default is 60000. format - the format to use, example: _Published %{s}._. Default is _%{s}_. */ init : function (i, format) { $("time, [datetime]").vakata_pretty_date(format); setInterval(function(){ $("time, [datetime]").vakata_pretty_date(format); }, i || 60000); } }; /* Function: $().vakata_pretty_date Sets the HTML of every element to the parsed difference of its _datetime_ attribute and the compare parameter. Parameters: format - makes it possible to modify the parsed string, example: _Published %{s}._. Default is _%{s}_. compare - the date to compare to. Default is the current date. */ $.fn.vakata_pretty_date = function (format, compare) { if(!format) { format = '%{s}'; } return this.each(function() { var $t = jQuery(this), date = $.vakata.pretty_date.parse($t.attr('datetime'), compare); if(date) { date = format.replace('%{s}', date); if($t.html() !== date) { $t.html(date); } } }); }; })(jQuery); /* Group: Selection Selection related functions */ (function ($) { /* Variable: $.vakata.selection *object* holds all selection related functions and properties. */ $.vakata.selection = { /* Function: $.vakata.selection.get Gets the current selection. Parameters: as_text - a boolean - if set to _true_ selection is returned as text, otherwise as HTML Returns: *string* - the current selection */ get : function (as_text) { if(window.getSelection) { if(as_text) { return window.getSelection().toString(); } var userSelection = window.getSelection(), range = userSelection.getRangeAt && userSelection.rangeCount ? userSelection.getRangeAt(0) : document.createRange(), div = document.createElement('div'); if(!userSelection.getRangeAt) { range.setStart(userSelection.anchorNode, userSelection.anchorOffset); range.setEnd(userSelection.focusNode, userSelection.focusOffset); } div.appendChild(range.cloneContents()); return div.innerHTML; } if(document.selection) { return document.selection.createRange()[ as_text ? 'text' : 'htmlText' ]; } return ''; }, /* Function: $.vakata.selection.elm_get Gets the selection inside an input element or textarea. Parameters: e - the actual DOM element or the ID of the element Returns: *object* - the current selection (start, end, length, text) */ elm_get : function (e) { e = typeof e === 'string' ? document.getElementById(e) : e; if(e.jquery) { e = e.get(0); } if('selectionStart' in e) { // Mozilla and DOM 3.0 return { 'start' : e.selectionStart, 'end' : e.selectionEnd, 'length' : (e.selectionEnd - e.selectionStart), 'text' : e.value.substr(e.selectionStart, (e.selectionEnd - e.selectionStart)) }; } else if(document.selection) { // IE e.focus(); var tr0 = document.selection.createRange(), tr1 = false, tr2 = false, len, text_whole, the_start, the_end; if(tr0 && tr0.parentElement() === e) { len = e.value.length; text_whole = e.value.replace(/\r\n/g, "\n"); tr1 = e.createTextRange(); tr1.moveToBookmark(tr0.getBookmark()); tr2 = e.createTextRange(); tr2.collapse(false); if(tr1.compareEndPoints("StartToEnd", tr2) > -1) { the_start = the_end = len; } else { the_start = -tr1.moveStart("character", -len); the_start += text_whole.slice(0, the_start).split("\n").length - 1; if (tr1.compareEndPoints("EndToEnd", tr2) > -1) { the_end = len; } else { the_end = -tr1.moveEnd("character", -len); the_end += text_whole.slice(0, the_end).split("\n").length - 1; } } text_whole = e.value.slice(the_start, the_end); return { 'start' : the_start, 'end' : the_end, 'length' : text_whole.length, 'text' : text_whole }; } } else { // not supported return { 'start' : e.value.length, 'end' : e.value.length, 'length' : 0, 'text' : '' }; } }, /* Function: $.vakata.selection.elm_set Sets the selection inside an input element or textarea. Parameters: e - the actual DOM element or the ID of the element beg - the char to start the selection end - the char to end the selection Returns: *object* - the current selection (start, end, length, text) */ elm_set : function (e, beg, end) { e = typeof e === 'string' ? document.getElementById(e) : e; if(e.jquery) { e = e.get(0); } if('selectionStart' in e) { // Mozilla and DOM 3.0 e.focus(); e.selectionStart = beg; e.selectionEnd = end; } else if(document.selection) { // IE e.focus(); var tr = e.createTextRange(), tx = e.value.replace(/\r\n/g, "\n"); beg -= tx.slice(0, beg).split("\n").length - 1; end -= tx.slice(0, end).split("\n").length - 1; tr.collapse(true); tr.moveEnd('character', end); tr.moveStart('character', beg); tr.select(); } return $.vakata.selection.elm_get(e); }, /* Function: $.vakata.selection.elm_replace Replace the selection inside an input element or textarea. Parameters: e - the actual DOM element or the ID of the element replace - the string to replace the selection with Returns: *object* - the current selection (start, end, length, text) */ elm_replace : function (e, replace) { e = typeof e === 'string' ? document.getElementById(e) : e; if(e.jquery) { e = e.get(0); } var sel = $.vakata.selection.elm_get(e), beg = sel.start, end = beg + replace.length; e.value = e.value.substr(0, beg) + replace + e.value.substr(sel.end, e.value.length); $.vakata.selection.elm_set(e, beg, end); return { 'start' : beg, 'end' : end, 'length' : replace.length, 'text' : replace }; }, /* Function: $.vakata.selection.elm_get_caret Returns the caret position in the element. Parameters: e - the actual DOM element or the ID of the element Returns: *number* - the current caret position */ elm_get_caret : function (e) { return $.vakata.selection.elm_get(e).end; }, /* Function: $.vakata.selection.elm_set_caret Sets the caret position in the element. Parameters: e - the actual DOM element or the ID of the element pos - the position to move the caret to Returns: *object* - the current selection */ elm_set_caret : function (e, pos) { return $.vakata.selection.elm_set(e, pos, pos); }, /* Function: $.vakata.selection.elm_get_caret_position Returns the caret position in pixels relative to the element. Parameters: e - the actual DOM element or the ID of the element Returns: *object* - the current position (with _left_ and _top_ values) */ elm_get_caret_position : function (e) { e = typeof e === 'string' ? document.getElementById(e) : e; if(e.jquery) { e = e.get(0); } var p = $.vakata.selection.elm_get_caret(e), s = e.value.substring(0, p).replace(/&/g,'&').replace(//ig,'>').replace(/\r/g, '').replace(/\t/g,' ').replace(/\n/ig, '
    '), b = $.vakata.get_scrollbar_width(), w = $(e).width(), h = $(e).height(); if(e.scrollHeight > h) { w -= b; } if(e.scrollWidth > w) { h -= b; } e = $(e); e = $('
    ').html(s).css({ 'background': 'red', 'width' : w + 'px', 'height' : 'auto', 'position' : 'absolute', 'left' : '0px', 'top' : '-10000px', 'fontSize' : e.css('fontSize'), 'fontFamily' : e.css('fontFamily'), 'fontWeight' : e.css('fontWeight'), 'fontVariant' : e.css('fontVariant'), 'fontStyle' : e.css('fontStyle'), 'textTransform' : e.css('textTransform'), 'lineHeight' : e.css('lineHeight'), 'whiteSpace' : 'pre-wrap' }); e.append(' ').appendTo('body'); s = e.find('span.caret'); p = s.offset(); p.top = p.top + 10000 + s.height(); e.remove(); return p; } }; })(jQuery); (function ($) { /* Function: $.vakata_highlight Hightlight words in the matched elements Parameters: settings - if a string is passed, it is used to search and highlight, if an array of strings is passed, each string is highlighted, you can also pass an object, containing a _words_ string or array, a _color_ string or array, a _css_class_ string. */ $.fn.vakata_highlight = function (settings) { var _return = this; if(typeof settings === 'string') { settings = [ settings ]; } if($.isArray(settings)) { settings = { 'words' : settings }; } settings = $.extend(true, {}, { 'css_class' : 'vakata-highlight', 'words' : [], 'color' : '#99ccff' }, settings); if(settings.words.length) { this.each(function () { var t = $(this); $.each(settings.words, function (i,v) { var color = false; if(typeof settings.color === 'string') { color = settings.color; } if($.isArray(settings.color) && typeof settings.color[i] === 'string') { color = settings.color[i]; } t .find(':vakata_icontains("' + v.replace(/\"/ig,'') + '")') .filter('strong, span, li, p, h1, h2, h3, h4, h5, h6, div, u, em, i, dt, dd') .contents() .filter(function() { return this.nodeType === 3; }) .each(function () { if(this.nodeValue.toLowerCase().indexOf(v.toLowerCase()) >= 0) { this.nodeValue = this.nodeValue.replace(new RegExp('(' + v.replace(/([\-.*+?\^${}()|\[\]\/\\])/g,"\\$1") + ')', 'ig'), '|{{{$1}}}|'); var o = $(this).parent(); o.html(o.html().replace(/\|\{\{\{/g,'').replace(/\}\}\}\|/g,'')); } }); }); }); } return _return; }; })(jQuery); (function ($) { var browser = {}, b_match = function(ua) { ua = ua.toLowerCase(); var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || /(webkit)[ \/]([\w.]+)/.exec( ua ) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || /(msie) ([\w.]+)/.exec( ua ) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || []; return { browser: match[1] || "", version: match[2] || "0" }; }, matched = b_match(navigator.userAgent); if(matched.browser) { browser[ matched.browser ] = true; browser.version = matched.version; } if(browser.chrome) { browser.webkit = true; } else if(browser.webkit) { browser.safari = true; } $.vakata.browser = browser; })(jQuery); /* * jsTree 1.0.0 * http://jstree.com/ * * Copyright (c) 2011 Ivan Bozhanov (vakata.com) * * Licensed same as jquery - under the terms of either the MIT License or the GPL Version 2 License * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * */ /*global jQuery, window, document, setTimeout, setInterval, clearTimeout, clearInterval, console */ /* File: jstree.js The only required part of jstree it consists of a few functions bound to the $.jstree object, the actual plugin function and a few core functions for manipulating a tree. */ (function () { "use strict"; if(!jQuery) { throw "jsTree: jQuery not included."; } if(jQuery.jstree) { return; } // prevent another load? maybe there is a better way? /* Group: $.jstree. Some static functions and variables, unless you know exactly what you are doing do not use these, but <$().jstree> instead. */ (function ($) { var instances = [], focused_instance = -1, plugins = {}, functions = {}; /* Variable: $.jstree *object* Contains all static functions and variables used by jstree, some plugins also append variables. */ $.jstree = { /* Variable: $.jstree.VERSION *string* the version of jstree */ VERSION : '1.0.0', /* Variable: $.jstree.IS_IE6 *boolean* indicating if the client is running Internet Explorer 6 */ IS_IE6 : ($.vakata.browser.msie && parseInt($.vakata.browser.version,10) === 6), /* Variable: $.jstree.IS_IE7 *boolean* indicating if the client is running Internet Explorer 7 */ IS_IE7 : ($.vakata.browser.msie && parseInt($.vakata.browser.version,10) === 6), /* Variable: $.jstree.IS_FF2 *boolean* indicating if the client is running Firefox 2 */ IS_FF2 : ($.vakata.browser.mozilla && parseFloat($.vakata.browser.version,10) < 1.9), /* Function: $.jstree.__construct Creates a new jstree instance, any arguments after the first one are merged and used to configure the tree. `.data("jstree")` is also called on the container and is used for configuration (keep in mind you can specify this data using a "data-jstree" attribute) Parameters: container - *mixed* the container of the tree (this should not be the UL node, but a wrapper) - DOM node, jQuery object or selector */ __construct : function (container) { var s = {}, // settings d = {}, // data p = [], // plugins t = [], // plugins temp i = 0; // index container = $(container); if($.jstree._reference(container)) { $.jstree.__destruct(container); } $.extend.apply(null, [true, s].concat(Array.prototype.slice.call(arguments, 1), (container.data("jstree") || {}) )); p = $.isArray(s.plugins) ? s.plugins : $.jstree.defaults.plugins.slice(); p = $.vakata.array_unique(p); s = $.extend(true, {}, $.jstree.defaults, s); $.each(plugins, function (i, val) { if(i !== "core" && $.inArray(i, p) === -1) { s[i] = null; delete s[i]; } else { t.push(i); d[i] = {}; } }); s.plugins = t; i = parseInt(instances.push({}),10) - 1; container .data("jstree_instance_id", i) .addClass("jstree jstree-" + i); this.data = d; this.get_index = function () { return i; }; this.get_container = function () { return container; }; this.get_container_ul = function () { return container.children("ul:eq(0)"); }; this.get_settings = function (writable) { return writable ? s : $.extend(true, {}, s); }; this.__trigger = function (ev, data) { if(!ev) { return; } if(!data) { data = {}; } if(typeof ev === "string") { ev = ev.replace(".jstree","") + ".jstree"; } data.inst = this; return this.get_container().triggerHandler(ev, data); }; instances[i] = this; $.each(t, function (j, val) { if(plugins[val]) { plugins[val].__construct.apply(instances[i]); } }); this.__trigger("__construct"); $.jstree._focus(i); return this; }, /* Function: $.jstree.__destruct Destroys an instance, and also clears `jstree-` prefixed classes and all events in the `jstree` namespace Parameters: instance - *mixed* the instance to destroy (this argument is passed to <$.jstree._reference> to get the instance) See also: <$.jstree._reference> */ __destruct : function (instance) { instance = $.jstree._reference(instance); if(!instance) { return false; } var s = instance.get_settings(), n = instance.get_index(), i = 0; if(focused_instance === n) { for(i in instances) { if(instances.hasOwnProperty(i) && i !== n) { $.jstree._focus(i); break; } } if(focused_instance === n) { $.jstree._focus(false); } } $.each(s.plugins, function (i, val) { try { plugins[val].__destruct.apply(instance); } catch(err) { } }); instance.__trigger("__destruct"); instance.get_container() .unbind(".jstree") .undelegate(".jstree") .removeData("jstree_instance_id") .find("[class^='jstree']") .addBack() .attr("class", function () { return this.className.replace(/jstree[^ ]*|$/ig,''); }); $(document) .unbind(".jstree-" + n) .undelegate(".jstree-" + n); delete instances[n]; return true; }, /* Function: $.jstree.__call Call a function on the instance and return the result Parameters: instance - *mixed* the instance to destroy (this argument is passed to <$.jstree._reference> to get the instance) operation - *string* the operation to execute args - *array* the arguments to pass to the function See also: <$.jstree._reference> */ __call : function (instance, operation, args) { instance = $.jstree._reference(instance); if(!instance || !$.isFunction(instance[operation])) { return; } return instance[operation].apply(instance, args); }, /* Function: $.jstree._reference Returns an instance Parameters: needle - *mixed* - integer, DOM node contained inside a jstree container, ID string, jQuery object, selector */ _reference : function (needle) { if(instances[needle]) { return instances[needle]; } var o = $(needle); if(!o.length && typeof needle === "string") { o = $("#" + needle); } if(!o.length) { return null; } return instances[o.closest(".jstree").data("jstree_instance_id")] || null; }, /* Function: $.jstree._focused Returns the currently focused instance (by default once an instance is created it is focused) */ _focused : function () { return instances[focused_instance] || null; }, /* Function: $.jstree._focus Make an instance focused (which defocuses the previously focused instance) Parameters: instance - *mixed* the instance to focus (this argument is passed to <$.jstree._reference> to get the instance) See also: <$.jstree._reference> */ _focus : function (instance) { if(instance === false) { instances[focused_instance].get_container().removeClass("jstree-focused"); instances[focused_instance].__trigger("_defocus"); focused_instance = -1; return false; } instance = $.jstree._reference(instance); if(!instance || instance.get_index() === focused_instance) { return false; } if(focused_instance !== -1) { instances[focused_instance].get_container().removeClass("jstree-focused"); instances[focused_instance].__trigger("_defocus"); } focused_instance = instance.get_index(); instance.get_container().addClass("jstree-focused"); instance.__trigger("_focus"); return true; }, /* Function: $.jstree.plugin Register a plugin Parameters: plugin_name - *string* the name of the new plugin (it will be used as a key in an object - make sure it is valid) plugin_data - *object* consists of 4 keys. Default is: >{ > __construct : $.noop, // this function will be executed when a new instance is created > __destuct : $.noop, // this function will be executed when an instance is destroyed > _fn : { }, // each key of this object should be a function that will extend the jstree prototype > defaults : false // the default configuration for the plugin (if any) >} */ plugin : function (plugin_name, plugin_data) { plugin_data = $.extend({}, { __construct : $.noop, __destuct : $.noop, _fn : { }, defaults : false }, plugin_data); plugins[plugin_name] = plugin_data; $.jstree.defaults[plugin_name] = plugin_data.defaults; $.each(plugin_data._fn, function (i, val) { val.plugin = plugin_name; val.old = functions[i]; functions[i] = function () { var rslt, func = val, args = Array.prototype.slice.call(arguments), evnt = new $.Event("before.jstree"), plgn = this.get_settings(true).plugins; do { if(func && func.plugin && $.inArray(func.plugin, plgn) !== -1) { break; } func = func.old; } while(func); if(!func) { return; } if(i.indexOf("_") === 0) { rslt = func.apply(this, args); } else { rslt = this.__trigger(evnt, { "func" : i, "args" : args, "plugin" : func.plugin }); if(rslt === false) { return; } rslt = func.apply( $.extend({}, this, { __callback : function (data) { this.__trigger( i, { "args" : args, "rslt" : data, "plugin" : func.plugin }); return data; }, __call_old : function (replace_arguments) { return func.old.apply(this, (replace_arguments ? Array.prototype.slice.call(arguments, 1) : args ) ); } }), args); } return rslt; }; functions[i].old = val.old; functions[i].plugin = plugin_name; }); }, /* Variable: $.jstree.defaults *object* storing all the default configuration options for every plugin and the core. If this is modified all instances created after the modification, which do not explicitly specify some other value will use the new default. Example: >// this instance will use the _default_ theme >$("#div0").jstree({ plugins : ["themes"] }); >$.jstree.defaults.themes.theme = "classic"; >// this instance will use the _classic_ theme >$("#div1").jstree({ plugins : ["themes"] }); >// this instance will use the _apple_ theme >$("#div2").jstree({ themes : { "theme" : "apple" }, plugins : ["themes"] }); */ defaults : { plugins : [] } }; /* Group: $().jstree() The actual plugin wrapper, use this to create instances or execute functions on created instances. Function: $().jstree Creates an instance using the specified objects for containers, or executes a command on an instance, specified by a container. Parameters: settings - *mixed* - if you pass an *object* a new instance will be created (using <$.jstree.__construct>) for each of the objects in the jquery collection, if an instance already exists on the container it will be destroyed first - if you pass a *string* it will be executed using <$.jstree.__call> on each instance Examples: > // this creates an instance > $("#some-id").jstree({ > plugins : [ "html_data", "themes", "ui" ] > }); > > // this executes a function on the instance > $("#some-id").jstree("select_node", "#the-id-to-select"); See also: <$.jstree.__construct>, <$.jstree.__destruct>, <$.jstree.__call> */ $.fn.jstree = function (settings) { var _is_method = (typeof settings === 'string'), _arguments = Array.prototype.slice.call(arguments, 1), _return = this; this.each(function () { if(_is_method) { var val = $.jstree.__call(this, settings, _arguments); if(typeof val !== "undefined" && (settings.indexOf("is_" === 0) || (val !== true && val !== false))) { _return = val; return false; } } else { _is_method = new $.jstree.__construct(this, settings); } }); return _return; }; functions = $.jstree.__construct.prototype; $.expr[':'].jstree = function(a,i,m) { return typeof ($(a).data("jstree_instance_id")) !== 'undefined'; }; })(jQuery); (function ($) { var ccp_node = false, ccp_mode = false; $(function() { $.jstree.SCROLLBAR_WIDTH = $.vakata.get_scrollbar_width(); }); $.jstree.plugin("core", { __construct : function () { this.data.core.rtl = (this.get_container().css("direction") === "rtl"); if(this.data.core.rtl) { this.get_container().addClass("jstree-rtl"); } this.data.core.ready = false; if($.support.touch) { this.get_container().addTouch(); } this.get_container() .bind("__construct.jstree", $.proxy(function () { // defer, so that events bound AFTER creating the instance (like __ready) are still handled setTimeout($.proxy(function () { if(this) { this.init(); } }, this), 0); }, this)) .bind("before.jstree", $.proxy(function (e, data) { if(!/^is_locked|unlock$/.test(data.func) && this.data.core.locked) { e.stopImmediatePropagation(); return false; } }, this)) .bind("create_node.jstree", $.proxy(function (e, data) { this.clean_node(data.rslt.obj); }, this)) .bind("load_node.jstree", $.proxy(function (e, data) { // data.rslt.status if(data.rslt.status) { if(data.rslt.obj === -1) { // only detach for root (checkbox three-state will not work otherwise) // also - if you could use async clean_node won't be such an issue var ul = this.get_container_ul().detach(); if(ul.children('li').length) { this.clean_node(ul.children('li')); } this.get_container().prepend(ul); } else { if(data.rslt.obj.find('> ul > li').length) { this.clean_node(data.rslt.obj.find('> ul > li')); } } if(!this.data.core.ready && !this.get_container_ul().find('.jstree-loading:eq(0)').length) { this.data.core.ready = true; this.__trigger("__ready"); } } }, this)) .bind("__loaded.jstree", $.proxy(function (e, data) { data.inst.get_container_ul().children('li').each(function () { data.inst.correct_node(this); }); }, this)) .bind("open_node.jstree", $.proxy(function (e, data) { data.rslt.obj.find('> ul > li').each(function () { data.inst.correct_node(this); }); }, this)) .bind("mousedown.jstree", $.proxy(function () { $.jstree._focus(this.get_index()); }, this)) .bind("dblclick.jstree", function () { if(document.selection && document.selection.empty) { document.selection.empty(); } else { if(window.getSelection) { var sel = window.getSelection(); try { sel.removeAllRanges(); sel.collapse(); } catch (er) { } } } }) .delegate(".jstree-ocl", "click.jstree", $.proxy(function (e) { // var trgt = $(e.target); // if(trgt.is("ins") && e.pageY - trgt.offset().top < this.data.core.li_height) { this.toggle_node(trgt); } this.toggle_node(e.target); }, this)); }, __destruct : function () { }, /* Class: jstree */ /* Variable: data *object* Provides storage for plugins (aside from private variables). Every plugin has an key in this object. > this.data.; This is useful for detecting if some plugin is included in the instance (plugins also use this for dependencies and enhancements). Function: get_index Returns an *integer*, which is the instance's index. Every instance on the page has an unique index, when destroying an intance the index will not be reused. Function: get_container Returns the jQuery extended container of the tree (the element you used when constructing the tree). Function: get_container_ul Returns the jQuery extended first UL node inside the container of the tree. Function: get_settings Returns the settings for the tree. Parameters: writable - *boolean* whether to return a copy of the settings object or a reference to it. Example: > $("#div1").jstree("get_settings"); // will return a copy > $.jstree._reference("#div1").get_settings(); // same as above > $.jstree._focused().get_settings(true); // a reference. BE CAREFUL! Function: __trigger Used internally to trigger events on the container node. Parameters: event_name - the name of the event to trigger (the *jstree* namespace will be appended to it) data - the additional object to pass along with the event. By default _data.inst_ will be the current instance, so when you bind to the event, you can access the instance easily. > $("div").bind("some-event.jstree", function (e, data) { data.inst.some_function(); }); */ /* Group: CORE options Variable: config.core.strings *mixed* used to store all localization strings. Default is _false_. Example 1: >$("div").jstree({ > core : { > strings : function (s) { > if(s === "Loading ...") { s = "Please wait ..."; } > return s; > } > } >}); Example 2: >$("div").jstree({ > core : { > strings : { > "Loading ..." : "Please wait ..." > } > } >}); See also: <_get_string> */ defaults : { strings : false, check_callback : true, animation : 100 }, _fn : { /* Group: CORE functions Function: _get_string Used to get the common string in the tree. If is set to a function, that function is called with a single parameter (the needed string), the response is returned. If is set to an object, the key named as the needed string is returned. If is not set, the the needed string is returned. Parameters: needed_string - *string* the needed string */ _get_string : function (s) { var a = this.get_settings(true).core.strings; if($.isFunction(a)) { return a.call(this, s); } if(a && a[s]) { return a[s]; } return s; }, /* Function: init Used internally. This function is called once the core plugin is constructed. Triggers: <__loaded> Event: __loaded This event is triggered in the *jstree* namespace when data is first rendered in the tree. It won't be triggered after a refresh. Fires only once. Parameters: data.inst - the instance Example: > $("div").bind("__loaded.jstree", function (e, data) { data.inst.do_something(); }); Event: __ready This event is triggered in the *jstree* namespace when all initial loading is done. It won't be triggered after a refresh. Fires only once. Parameters: data.inst - the instance */ init : function () { this.data.core.original_container_html = this.get_container().find(" > ul > li").clone(true); this.data.core.original_container_html.find("li").addBack().contents().filter(function() { return this.nodeType === 3 && (!this.nodeValue || /^\s+$/.test(this.nodeValue)); }).remove(); this.get_container().html(""); this.clean_node(-1); this.data.core.li_height = this.get_container_ul().children("li:eq(0)").height() || 18; this.load_node(-1, function () { this.__trigger("__loaded"); }); }, /* Function: lock Used to lock the tree. When the tree is in a locked state, no functions can be called on the instance (except and ). Additionally a _jstree-locked_ class is applied on the container. Triggers: Event: lock This event is triggered in the *jstree* namespace when the tree is locked. Parameters: data.inst - the instance data.args - *array* the arguments passed to the function data.plugin - *string* the function's plugin (here it will be _"core"_ but if the function is extended it may be something else). data.rslt - _null_ Example: > $("div").bind("lock.jstree", function (e, data) { data.inst.do_something(); }); */ lock : function () { this.data.core.locked = true; this.get_container().addClass("jstree-locked"); this.__callback(); }, /* Function: unlock Used to unlock the tree. Instance can be used normally again. The _jstree-locked_ class is removed from the container. Triggers: Event: unlock This event is triggered in the *jstree* namespace when the tree is unlocked. Parameters: data.inst - the instance data.args - *array* the arguments passed to the function data.plugin - *string* the function's plugin (here it will be _"core"_ but if the function is extended it may be something else). data.rslt - _null_ Example: > $("div").bind("unlock.jstree", function (e, data) { data.inst.do_something(); }); */ unlock : function () { this.data.core.locked = false; this.get_container().removeClass("jstree-locked"); this.__callback(); }, /* Function: is_locked Used to get the locked status of the tree. Returns: locked - *boolean* _true_ if tree is locked, _false_ otherwise */ is_locked : function () { return this.data.core.locked; }, /* Function: get_node Get a hold of the LI node (which represents the jstree node). Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. Returns: jquery collection - node was found, the collection contains the LI node -1 - the tree container was referenced false - on failure (obj is not part of a tree, or does not exists in the DOM) */ get_node : function (obj) { var $obj = $(obj, this.get_container()); if($obj.is(".jstree") || obj === -1) { return -1; } $obj = $obj.closest("li", this.get_container()); return $obj.length ? $obj : false; }, /* Function: get_next Get the next sibling of a node Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. strict - *boolean* if set to _true_ jstree will only return immediate siblings, otherwise, if _obj_ is the last child of its parent, the parent's next sibling is returned. Returns: jquery collection - node was found, the collection contains the LI node -1 - the tree container was referenced false - node was not found, or failure (obj is not part of a tree, or does not exists in the DOM) */ get_next : function (obj, strict) { obj = this.get_node(obj); if(obj === -1) { return this.get_container_ul().children("li:eq(0)"); } if(!obj || !obj.length) { return false; } if(strict) { return (obj.nextAll("li").size() > 0) ? obj.nextAll("li:eq(0)") : false; } if(obj.hasClass("jstree-open")) { return obj.find("li:eq(0)"); } else if(obj.nextAll("li").size() > 0) { return obj.nextAll("li:eq(0)"); } else { return obj.parentsUntil(".jstree","li").next("li").eq(0); } }, /* Function: get_prev Get the previous sibling of a node Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. strict - *boolean* if set to _true_ jstree will only return immediate siblings, otherwise, if _obj_ is the first child of its parent, the parent's previous sibling is returned. Returns: jquery collection - node was found, the collection contains the LI node -1 - the tree container was referenced false - node was not found, or failure (obj is not part of a tree, or does not exists in the DOM) */ get_prev : function (obj, strict) { obj = this.get_node(obj); if(obj === -1) { return this.get_container().find("> ul > li:last-child"); } if(!obj || !obj.length) { return false; } if(strict) { return (obj.prevAll("li").length > 0) ? obj.prevAll("li:eq(0)") : false; } if(obj.prev("li").length) { obj = obj.prev("li").eq(0); while(obj.hasClass("jstree-open")) { obj = obj.children("ul:eq(0)").children("li:last"); } return obj; } else { var o = obj.parentsUntil(".jstree","li:eq(0)"); return o.length ? o : false; } }, /* Function: get_parent Get the parent of a node Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. Returns: jquery collection - node was found, the collection contains the LI node -1 - when _obj_ was a root node false - on failure (obj is not part of a tree, or does not exists in the DOM) */ get_parent : function (obj) { obj = this.get_node(obj); if(obj === -1 || !obj || !obj.length) { return false; } var o = obj.parentsUntil(".jstree", "li:eq(0)"); return o.length ? o : -1; }, /* Function: get_children Get all the children of a node Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. If _-1_ is used all root nodes are returned. Returns: jquery collection - node was found, the collection contains the LI nodes of all immediate children false - on failure (obj is not part of a tree, or does not exists in the DOM) */ get_children : function (obj) { obj = this.get_node(obj); if(obj === -1) { return this.get_container_ul().children("li"); } if(!obj || !obj.length) { return false; } return obj.find("> ul > li"); }, /* Function: is_parent Check if a node is a parent. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. Returns: true - _obj_ has children or is closed (will be loaded) false - _obj_ is not a valid node or has no children (leaf node) */ is_parent : function (obj) { obj = this.get_node(obj); return obj && obj !== -1 && (obj.find("> ul > li:eq(0)").length || obj.hasClass("jstree-closed")); }, /* Function: is_loaded Check if a node is loaded. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. Returns: true - _obj_ has children or is leaf false - _obj_ is currently loading or is not a leaf, but has no children */ is_loaded : function (obj) { obj = this.get_node(obj); return obj && ( (obj === -1 && !this.get_container().find("> ul > li.jstree-loading").length) || ( obj !== -1 && !obj.hasClass('jstree-loading') && (obj.find('> ul > li').length || obj.hasClass('jstree-leaf')) ) ); }, /* Function: is_loading Check if a node is currently loading. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. Returns: true - _obj_ is currently loading false - _obj_ is not currently loading */ is_loading : function (obj) { obj = this.get_node(obj); return obj && ( (obj === -1 && this.get_container().find("> ul > li.jstree-loading").length) || (obj !== -1 && obj.hasClass("jstree-loading")) ); }, /* Function: is_open Check if a node is currently open. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. Returns: true - _obj_ is currently open false - _obj_ is not currently open */ is_open : function (obj) { obj = this.get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-open"); }, /* Function: is_closed Check if a node is currently closed. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. Returns: true - _obj_ is currently closed false - _obj_ is not currently closed */ is_closed : function (obj) { obj = this.get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-closed"); }, /* Function: is_leaf Check if a node is a leaf node (has no children). Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. Returns: true - _obj_ is a leaf node false - _obj_ is not a leaf node */ is_leaf : function (obj) { obj = this.get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-leaf"); }, /* Function: load_node Load the children of a node. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. Use -1 to load the root nodes. callback - a function to be executed in the tree's scope. Receives two arguments: _obj_ (the same node used to call load_node), _status_ (a boolean indicating if the node was loaded successfully. Returns: true - _obj_ is a valid node and will try loading it false - _obj_ is not a valid node Triggers: See also: <_load_node> Event: load_node This event is triggered in the *jstree* namespace when a node is loaded (succesfully or not). Parameters: data.inst - the instance data.args - *array* the arguments passed to the function data.plugin - *string* the function's plugin (here it will be _"core"_ but if the function is extended it may be something else). data.rslt - *object* which contains two keys _obj_ (the loaded node) and _status_ - whether the node was loaded successfully. Example: > $("div").bind("load_node.jstree", function (e, data) { if(data.rslt.status) { data.inst.open_node(data.rslt.obj); } }); */ load_node : function (obj, callback) { obj = this.get_node(obj); if(!obj) { callback.call(this, obj, false); return false; } // if(this.is_loading(obj)) { return true; } if(obj !== -1) { obj.addClass("jstree-loading"); } this._load_node(obj, $.proxy(function (status) { if(obj !== -1) { obj.removeClass("jstree-loading"); } this.__callback({ "obj" : obj, "status" : status }); if(callback) { callback.call(this, obj, status); } }, this)); return true; }, /* Function: _load_node Load the children of a node, but as opposed to does not change any visual properties or trigger events. This function is used in internally. The idea is for data source plugins to overwrite this function. This implementation (from the *core*) only uses markup found in the tree container, and does not load async. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. Use -1 to load the root nodes. callback - a function to be executed in the tree's scope. Receives one argument: _status_ (a boolean indicating if the node was loaded successfully). */ _load_node : function (obj, callback) { // if using async - empty the node first if(obj === -1) { this.get_container_ul().empty().append(this.data.core.original_container_html.clone(true)); } callback.call(null, true); }, /* Function: open_node Open a node so that its children are visible. If the node is not loaded try loading it first. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. callback - a function to be executed in the tree's scope. Receives two arguments: _obj_ (the node being opened) and _status_ (a boolean indicating if the node was opened successfully). animation - the duration in miliseconds of the slideDown animation. If not supplied the jQuery default is used. Please note that on IE6 a _0_ is enforced here due to performance issues. Triggers: , <__after_open> Event: open_node This event is triggered in the *jstree* namespace when a node is successfully opened (but if animation is used this event is triggered BEFORE the animation completes). See <__after_open>. Parameters: data.inst - the instance data.args - *array* the arguments passed to the function data.plugin - *string* the function's plugin (here it will be _"core"_ but if the function is extended it may be something else). data.rslt - *object* which contains a single key: _obj_ (the opened node). Example: > $("div").bind("open_node.jstree", function (e, data) { > data.rslt.obj.find('> ul > .jstree-closed').each(function () { > data.inst.open_node(this); > } > }); Event: __after_open This event is triggered in the *jstree* namespace when a node is successfully opened AFTER the animation completes). See . Parameters: data.inst - the instance data.rslt - *object* which contains a single key: _obj_ (the opened node). Example: > $("div").bind("__after_open.jstree", function (e, data) { > data.rslt.obj.find('> ul > .jstree-closed').each(function () { > data.inst.open_node(this); > } > }); */ open_node : function (obj, callback, animation) { obj = this.get_node(obj); animation = (typeof animation).toLowerCase() === "undefined" ? this.get_settings().core.animation : animation; if(obj === -1 || !obj || !obj.length) { return false; } if(!this.is_closed(obj)) { if(callback) { callback.call(this, obj, false); } return false; } if(!this.is_loaded(obj)) { // TODO: is_loading? this.load_node(obj, function (o, ok) { return ok ? this.open_node(o, callback, animation) : callback ? callback.call(this, o, false) : false; }); } else { var t = this; obj .children("ul").css("display","none").end() .removeClass("jstree-closed").addClass("jstree-open") // .children("ins").text("-").end() .children("ul").stop(true, true).slideDown( ($.jstree.IS_IE6 ? 0 : animation), function () { this.style.display = ""; t.__trigger("__after_open", { "rslt" : { "obj" : obj } }); }); if(callback) { callback.call(this, obj, true); } this.__callback({ "obj" : obj }); } }, /* Function: close_node Close a node so that its children are not visible. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. animation - the duration in miliseconds of the slideDown animation. If not supplied the jQuery default is used. Please note that on IE6 a _0_ is enforced here due to performance issues. Triggers: , <__after_close> Event: close_node This event is triggered in the *jstree* namespace when a node is closed (but if animation is used this event is triggered BEFORE the animation completes). See <__after_close>. Parameters: data.inst - the instance data.args - *array* the arguments passed to the function data.plugin - *string* the function's plugin (here it will be _"core"_ but if the function is extended it may be something else). data.rslt - *object* which contains a single key: _obj_ (the closed node). Example: > $("div").bind("close_node.jstree", function (e, data) { > data.rslt.obj.children('ul').remove(); > }); Event: __after_close This event is triggered in the *jstree* namespace when a node is closed AFTER the animation completes). See . Parameters: data.inst - the instance data.rslt - *object* which contains a single key: _obj_ (the opened node). Example: > $("div").bind("__after_close.jstree", function (e, data) { > data.rslt.obj.children('ul').remove(); > }); */ close_node : function (obj, animation) { obj = this.get_node(obj); animation = (typeof animation).toLowerCase() === "undefined" ? this.get_settings().core.animation : animation; if(!obj || !obj.length || !this.is_open(obj)) { return false; } var t = this; obj .children("ul").attr("style","display:block !important").end() .removeClass("jstree-open").addClass("jstree-closed") // .children("ins").text("+").end() .children("ul").stop(true, true).slideUp( ($.jstree.IS_IE6 ? 0 : animation), function () { this.style.display = ""; t.__trigger("__after_close", { "rslt" : { "obj" : obj } }); }); this.__callback({ "obj" : obj }); }, /* Function: toggle_node If a node is closed - open it, if it is open - close it. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. */ toggle_node : function (obj) { if(this.is_closed(obj)) { return this.open_node(obj); } if(this.is_open(obj)) { return this.close_node(obj); } }, /* Function: open_all Open all nodes from a certain node down. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. If _-1_ is used or is omitted all nodes in the tree are opened. animation - the duration of the slideDown animation when opening the nodes. If not set _0_ is enforced for performance issues. original_obj - used internally to keep track of the recursion - do not set manually! Triggers: Event: open_all This event is triggered in the *jstree* namespace when an open_all call completes. Parameters: data.inst - the instance data.args - *array* the arguments passed to the function data.plugin - *string* the function's plugin (here it will be _"core"_ but if the function is extended it may be something else). data.rslt - *object* which contains a single key: _obj_ (the node used in the call). Example: > $("div").bind("open_all.jstree", function (e, data) { > alert('DONE'); > }); */ open_all : function (obj, animation, original_obj) { obj = obj ? this.get_node(obj) : -1; obj = !obj || obj === -1 ? this.get_container_ul() : obj; original_obj = original_obj || obj; var _this = this; obj = this.is_closed(obj) ? obj.find('li.jstree-closed').addBack() : obj.find('li.jstree-closed'); obj.each(function () { _this.open_node( this, _this.is_loaded(this) ? false : function(obj) { this.open_all(obj, animation, original_obj); }, animation || 0 ); }); if(original_obj.find('li.jstree-closed').length === 0) { this.__callback({ "obj" : original_obj }); } }, /* Function: close_all Close all nodes from a certain node down. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. If _-1_ is used or is omitted all nodes in the tree are closed. animation - the duration of the slideDown animation when closing the nodes. If not set _0_ is enforced for performance issues. Triggers: Event: close_all This event is triggered in the *jstree* namespace when a close_all call completes. Parameters: data.inst - the instance data.args - *array* the arguments passed to the function data.plugin - *string* the function's plugin (here it will be _"core"_ but if the function is extended it may be something else). data.rslt - *object* which contains a single key: _obj_ (the node used in the call). Example: > $("div").bind("close_all.jstree", function (e, data) { > alert('DONE'); > }); */ close_all : function (obj, animation) { obj = obj ? this._get_node(obj) : -1; var $obj = !obj || obj === -1 ? this.get_container_ul() : obj, _this = this; $obj = this.is_open($obj) ? $obj.find('li.jstree-open').addBack() : $obj.find('li.jstree-open'); $obj.each(function () { _this.close_node(this, animation || 0); }); this.__callback({ "obj" : obj }); }, /* Function: clean_node This function converts inserted nodes to the required by jsTree format. It takes care of converting a simple unodreder list to the internally used markup. The core calls this function automatically when new data arrives (by binding to the event). Each plugin may override this function to include its own source, but keep in mind to do it like that: > clean_node : function(obj) { > obj = this.__call_old(); > obj.each(function () { > // do your stuff here > }); > } Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. If _-1_ is used or is omitted all nodes in the tree are cleaned. Returns: jQuery collection - the cleaned children of the original node. */ clean_node : function (obj) { // DETACH maybe inside the "load_node" function? But what about animations, etc? obj = this.get_node(obj); obj = !obj || obj === -1 ? this.get_container().find("li") : obj.find("li").addBack(); var _this = this; return obj.each(function () { var t = $(this), d = t.data("jstree"), // is_ajax -> return this.get_settings().core.is_ajax || this.data.ajax; s = (d && d.opened) || t.hasClass("jstree-open") ? "open" : (d && d.closed) || t.children("ul").length || (d && d.children) ? "closed" : "leaf"; // replace with t.find('>ul>li').length || (this.is_ajax() && !t.children('ul').length) if(d && d.opened) { delete d.opened; } if(d && d.closed) { delete d.closed; } t.removeClass("jstree-open jstree-closed jstree-leaf jstree-last"); if(!t.children("a").length) { // allow for text and HTML markup inside the nodes t.contents().filter(function() { return this.nodeType === 3 || this.tagName !== 'UL'; }).wrapAll(''); // TODO: make this faster t.children('a').html(t.children('a').html().replace(/[\s\t\n]+$/,'')); } else { if(!$.trim(t.children('a').attr('href'))) { t.children('a').attr("href","#"); } } if(!t.children("ins.jstree-ocl").length) { t.prepend(" "); } if(t.is(":last-child")) { t.addClass("jstree-last"); } switch(s) { case 'leaf': t.addClass('jstree-leaf'); break; case 'closed': t.addClass('jstree-open'); _this.close_node(t, 0); break; case 'open': t.addClass('jstree-closed'); _this.open_node(t, false, 0); break; } }); }, /* Function: correct_node This function corrects the open/closed/leaf state as data changes (as the user interacts with the tree). The core calls this function automatically when a node is opened, deleted or moved. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. If _-1_ is used or is omitted the root nodes are processed. Returns: jQuery collection - the processed children of the original node. */ /* PROCESS SINGLE NODE (OR USE BOOLEAN single PARAM), CALL FROM CLEAN_NODE, LOSE THE EVENTS ABOVE */ correct_node : function (obj, deep) { obj = this.get_node(obj); if(!obj || (obj === -1 && !deep)) { return false; } if(obj === -1) { obj = this.get_container().find('li'); } else { obj = deep ? obj.find('li').addBack() : obj; } obj.each(function () { var obj = $(this); switch(!0) { case obj.hasClass("jstree-open") && !obj.find("> ul > li").length: obj.removeClass("jstree-open").addClass("jstree-leaf").children("ul").remove(); // children("ins").html(" ").end() break; case obj.hasClass("jstree-leaf") && !!obj.find("> ul > li").length: obj.removeClass("jstree-leaf").addClass("jstree-closed"); //.children("ins").html("+"); break; } obj[obj.is(":last-child") ? 'addClass' : 'removeClass']("jstree-last"); }); return obj; }, /* Function: scroll_to_node This function scrolls the container to the desired node (if needed). Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. */ scroll_to_node : function (obj) { var c = this.get_container()[0], t; if(c.scrollHeight > c.offsetHeight) { obj = this.get_node(obj); if(!obj || obj === -1 || !obj.length || !obj.is(":visible")) { return; } t = obj.offset().top - this.get_container().offset().top; if(t < 0) { c.scrollTop = c.scrollTop + t - 1; } if(t + this.data.core.li_height + (c.scrollWidth > c.offsetWidth ? $.jstree.SCROLLBAR_WIDTH : 0) > c.offsetHeight) { c.scrollTop = c.scrollTop + (t - c.offsetHeight + this.data.core.li_height + 1 + (c.scrollWidth > c.offsetWidth ? $.jstree.SCROLLBAR_WIDTH : 0)); } } }, /* Function: get_state This function returns the current state of the tree (as collected from all active plugins). Plugin authors: pay special attention to the way this function is extended for new plugins. In your plugin code write: > get_state : function () { > var state = this.__call_old(); > state.your-plugin-name = ; > return state; > } Returns: object - the current state of the instance */ get_state : function () { // TODO: scroll position, theme var state = { 'open' : [], 'scroll' : { 'left' : this.get_container().scrollLeft(), 'top' : this.get_container().scrollTop() } }; this.get_container_ul().find('.jstree-open').each(function () { if(this.id) { state.open.push(this.id); } }); return state; }, /* Function: set_state This function returns sets the state of the tree. Plugin authors: pay special attention to the way this function is extended for new plugins. In your plugin code write: > set_state : function (state, callback) { > if(this.__call_old()) { > if(state.your-plugin-name) { > > // restore using `state.your-plugin-name` > // if you need some async activity so that you return to this bit of code > // do not delete state.your-plugin-name and return false (see core's function for example) > > delete state.your-plugin-name; > this.set_state(state, callback); > return false; > } > return true; > } > return false; > } Parameters: state - *object* the state to restore to callback - *function* this will be executed in the instance's scope once restoring is done Returns: boolean - the return value is used to determine the phase of restoration Triggers: Event: set_state This event is triggered in the *jstree* namespace when a set_state call completes. Parameters: data.inst - the instance data.args - *array* the arguments passed to the function data.plugin - *string* the function's plugin (here it will be _"core"_ but if the function is extended it may be something else) */ set_state : function (state, callback) { if(state) { if($.isArray(state.open)) { var res = true, t = this; //this.close_all(); $.each(state.open.concat([]), function (i, v) { v = document.getElementById(v); if(v) { if(t.is_loaded(v)) { if(t.is_closed(v)) { t.open_node(v, false, 0); } $.vakata.array_remove(state.open, i); } else { if(!t.is_loading(v)) { t.open_node(v, $.proxy(function () { this.set_state(state); }, t), 0); } // there will be some async activity - so wait for it res = false; } } }); if(res) { delete state.open; this.set_state(state, callback); } return false; } if(state.scroll) { if(state.scroll && typeof state.scroll.left !== 'undefined') { this.get_container().scrollLeft(state.scroll.left); } if(state.scroll && typeof state.scroll.top !== 'undefined') { this.get_container().scrollTop(state.scroll.top); } delete state.scroll; delete state.open; this.set_state(state, callback); return false; } if($.isEmptyObject(state)) { if(callback) { callback.call(this); } this.__callback(); return false; } return true; } return false; }, /* Function: refresh This function saves the current state, reloads the complete tree and returns it to the saved state. Triggers: Event: refresh This event is triggered in the *jstree* namespace when a refresh call completes. Parameters: data.inst - the instance */ refresh : function () { this.data.core.state = this.get_state(); this.load_node(-1, function (o, s) { if(s) { this.set_state($.extend(true, {}, this.data.core.state), function () { this.__trigger('refresh'); }); } this.data.core.state = null; }); }, /* Function: get_text This function returns the title of the node. Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. remove_html - *boolean* set to _true_ to return plain text instead of HTML Returns: string - the title of the node, specified by _obj_ */ get_text : function (obj, remove_html) { obj = this.get_node(obj); if(!obj || obj === -1 || !obj.length) { return false; } obj = obj.children("a:eq(0)").clone(); obj.children(".jstree-icon").remove(); obj = obj[ remove_html ? 'text' : 'html' ](); obj = $('
    ')[ remove_html ? 'text' : 'html' ](obj); return obj.html(); }, /* Function: set_text This function sets the title of the node. This is a low-level function, you'd be better off using . Parameters: obj - *mixed* this is used as a jquery selector - can be jQuery object, DOM node, string, etc. val - *string* the new title of the node (can be HTMl too) Returns: boolean - was the rename successfull Triggers: Event: set_text This event is triggered in the *jstree* namespace when a set_text call completes. Parameters: data.inst - the instance data.args - *array* the arguments passed to the function data.plugin - *string* the function's plugin (here it will be _"core"_ but if the function is extended it may be something else) data.rslt - *object* which contains a two keys: _obj_ (the node) and _val_ (the new title). Example: > $("div").bind("set_text.jstree", function (e, data) { > alert("Renamed to: " + data.rslt.val); > }); */ set_text : function (obj, val) { obj = this.get_node(obj); if(!obj || obj === -1 || !obj.length) { return false; } obj = obj.children("a:eq(0)"); var tmp = obj.children("INS").clone(); obj.html(val).prepend(tmp); this.__callback({ "obj" : obj, "text" : val }); return true; }, /* Function: parse_json This function returns a jQuery node after parsing a JSON object (a LI node for single elements or an UL node for multiple). This function will use the default title from if none is specified. Parameters: node - *mixed* the input to parse > // can be a string > "The title of the parsed node" > // array of strings > [ "Node 1", "Node 2" ] > // an object > { "title" : "The title of the parsed node" } > // you can manipulate the output > { "title" : "The title of the parsed node", "li_attr" : { "id" : "id_for_li" }, "a_attr" : { "href" : "http://jstree.com" } } > // you can supply metadata, which you can later access using $(the_li_node).data() > { "title" : "The title of the parsed node", "data" : { } } > // you can supply children (they can be objects too) > { "title" : "The title of the parsed node", "children" : [ "Node 1", { "title" : "Node 2" } ] } Returns: jQuery - the LI (or UL) node which was produced from the JSON */ parse_json : function (node) { var li, a, ul, t; if(node === null || ($.isArray(node) && node.length === 0)) { return false; } if($.isArray(node)) { ul = $("