PageRenderTime 61ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/vakata.js

https://github.com/CNDLS/jstree
JavaScript | 1526 lines | 878 code | 37 blank | 611 comment | 188 complexity | a5fa2f1fd5abe314a5ecd51eacacbc2d MD5 | raw file
  1. /*
  2. File: Helper functions
  3. This file includes some functions that enable CSS manipulations, contextmenus, XSLT transformations and drag'n'drop.
  4. All of those work independently of jstree.
  5. */
  6. /*
  7. Variable: $.vakata
  8. *object* Holds all helper objects.
  9. */
  10. (function ($) {
  11. $.vakata = {};
  12. })(jQuery);
  13. /*
  14. Group: Miscellaneous
  15. Various small snippets.
  16. */
  17. /*
  18. Function: $().vakata_reverse
  19. Makes it possible to apply the standard array reverse function to a jQuery collection.
  20. Input:
  21. > <div>1</div><div>2</div><div>3</div>
  22. > $("div").vakata_reverse().each(function () { document.write(this.innerHTML); });
  23. Output:
  24. >321
  25. */
  26. (function ($) {
  27. $.fn.vakata_reverse = [].reverse;
  28. })(jQuery);
  29. /*
  30. Function: $.vakata.array_remove
  31. Makes it possible to remove an item (or a group of items) form an array.
  32. http://ejohn.org/blog/javascript-array-remove/
  33. Input:
  34. > $.vakata.array_remove(['a', 'b', 'c'], 1);
  35. Output:
  36. >['a', 'c']
  37. */
  38. (function ($) {
  39. $.vakata.array_remove = function(array, from, to) {
  40. var rest = array.slice((to || from) + 1 || array.length);
  41. array.length = from < 0 ? array.length + from : from;
  42. array.push.apply(array, rest);
  43. return array;
  44. };
  45. })(jQuery);
  46. /*
  47. Function: $.vakata.array_unique
  48. Returns only the unique items from an array.
  49. Input:
  50. > $.vakata.array_unique(['c','a','a','b','c','b']);
  51. Output:
  52. >['a', 'b', 'c']
  53. */
  54. (function ($) {
  55. $.vakata.array_unique = function(array) {
  56. var a = [], i, j, l;
  57. for(i = 0, l = array.length; i < l; i++) {
  58. for(j = 0; j <= i; j++) {
  59. if(array[i] === array[j]) {
  60. break;
  61. }
  62. }
  63. if(j === i) { a.push(array[i]); }
  64. }
  65. return a;
  66. };
  67. })(jQuery);
  68. /*
  69. Function: $.vakata.attributes
  70. Collects all attributes from a DOM node.
  71. */
  72. (function ($) {
  73. $.vakata.attributes = function(node, with_values) {
  74. node = $(node)[0];
  75. var attr = with_values ? {} : [];
  76. $.each(node.attributes, function (i, v) {
  77. if($.inArray(v.nodeName.toLowerCase(),['style','contenteditable','hasfocus','tabindex']) !== -1) { return; }
  78. if(v.nodeValue !== null && $.trim(v.nodeValue) !== '') {
  79. if(with_values) { attr[v.nodeName] = v.nodeValue; }
  80. else { attr.push(v.nodeName); }
  81. }
  82. });
  83. return attr;
  84. };
  85. })(jQuery);
  86. /*
  87. Group: CSS
  88. Functions needed to manipulate stylesheets (add, remove, change)
  89. */
  90. (function ($) {
  91. /*
  92. Variable: $.vakata.css
  93. *object* holds all CSS related functions
  94. */
  95. $.vakata.css = {
  96. /*
  97. Function: $.vakata.css.get_css
  98. Retrieves or deletes a specific rule.
  99. Parameters:
  100. rule_name - *string* the rule to search for (any CSS rule)
  101. delete_flag - *boolean* whether you want to delete or simply retrieve a reference to the rule
  102. sheet - the sheet to search in (do not specify this to search in all sheets)
  103. Returns either:
  104. a reference to the rule - if it was found and the delete flag was not set
  105. true - if the delete flag was set and the rule was successfully removed
  106. false - if the rule could not be found
  107. See also:
  108. <$.vakata.css.remove_css>
  109. */
  110. get_css : function(rule_name, delete_flag, sheet) {
  111. rule_name = rule_name.toLowerCase();
  112. var css_rules = sheet.cssRules || sheet.rules,
  113. j = 0;
  114. do {
  115. if(css_rules.length && j > css_rules.length + 5) { return false; }
  116. if(css_rules[j].selectorText && css_rules[j].selectorText.toLowerCase() == rule_name) {
  117. if(delete_flag === true) {
  118. if(sheet.removeRule) { sheet.removeRule(j); }
  119. if(sheet.deleteRule) { sheet.deleteRule(j); }
  120. return true;
  121. }
  122. else { return css_rules[j]; }
  123. }
  124. }
  125. while (css_rules[++j]);
  126. return false;
  127. },
  128. /*
  129. Function: $.vakata.css.add_css
  130. Adds a rule.
  131. Parameters:
  132. rule_name - *string* the rule to add
  133. sheet - a reference to the sheet to add to
  134. Returns either:
  135. a reference to the rule - if the rule was added
  136. false - if the rule could not be added, or if such a rule already exists
  137. */
  138. add_css : function(rule_name, sheet) {
  139. if($.jstree.css.get_css(rule_name, false, sheet)) { return false; }
  140. if(sheet.insertRule) { sheet.insertRule(rule_name + ' { }', 0); } else { sheet.addRule(rule_name, null, 0); }
  141. return $.vakata.css.get_css(rule_name);
  142. },
  143. /*
  144. Function: $.vakata.css.remove_css
  145. Removes a rule, this functions is a shortcut to <$.vakata.css.get_css> with the delete flag set to true.
  146. Parameters:
  147. rule_name - *string* the rule to remove
  148. sheet - the sheet to remove from (you can omit this and all sheets will be searched)
  149. Returns either:
  150. true - if rule was removed
  151. false - if the rule could not be removed
  152. See also:
  153. <$.vakata.css.get_css>
  154. */
  155. remove_css : function(rule_name, sheet) {
  156. return $.vakata.css.get_css(rule_name, true, sheet);
  157. },
  158. /*
  159. Function: $.vakata.css.add_sheet
  160. Adds a whole stylesheet or appends to an existing stylesheet.
  161. Parameters:
  162. options - *object*:
  163. 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
  164. options.str - text content of the stylesheet - when this is supplied _options.url_ is not used. A STYLE element is used.
  165. 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.
  166. Returns:
  167. a reference to the stylesheet
  168. */
  169. add_sheet : function(opts) {
  170. var tmp = false, is_new = true;
  171. if(opts.str) {
  172. if(opts.title) { tmp = $("style[id='" + opts.title + "-stylesheet']")[0]; }
  173. if(tmp) { is_new = false; }
  174. else {
  175. tmp = document.createElement("style");
  176. tmp.setAttribute('type',"text/css");
  177. if(opts.title) { tmp.setAttribute("id", opts.title + "-stylesheet"); }
  178. }
  179. if(tmp.styleSheet) {
  180. if(is_new) {
  181. document.getElementsByTagName("head")[0].appendChild(tmp);
  182. tmp.styleSheet.cssText = opts.str;
  183. }
  184. else {
  185. tmp.styleSheet.cssText = tmp.styleSheet.cssText + " " + opts.str;
  186. }
  187. }
  188. else {
  189. tmp.appendChild(document.createTextNode(opts.str));
  190. document.getElementsByTagName("head")[0].appendChild(tmp);
  191. }
  192. return tmp.sheet || tmp.styleSheet;
  193. }
  194. if(opts.url) {
  195. if(document.createStyleSheet) {
  196. try { tmp = document.createStyleSheet(opts.url); } catch (e) { }
  197. }
  198. else {
  199. tmp = document.createElement('link');
  200. tmp.rel = 'stylesheet';
  201. tmp.type = 'text/css';
  202. tmp.media = "all";
  203. tmp.href = opts.url;
  204. document.getElementsByTagName("head")[0].appendChild(tmp);
  205. return tmp.styleSheet;
  206. }
  207. }
  208. }
  209. };
  210. })(jQuery);
  211. /*
  212. Group: Drag'n'drop
  213. Functions needed to drag'n'drop elements
  214. */
  215. (function ($) {
  216. // private variable
  217. var vakata_dnd = {
  218. element : false,
  219. is_down : false,
  220. is_drag : false,
  221. helper : false,
  222. helper_w: 0,
  223. data : false,
  224. init_x : 0,
  225. init_y : 0,
  226. scroll_l: 0,
  227. scroll_t: 0,
  228. scroll_e: false,
  229. scroll_i: false
  230. };
  231. /*
  232. Variable: $.vakata.dnd
  233. *object* holds all DND related functions
  234. */
  235. $.vakata.dnd = {
  236. /*
  237. Variable: $.vakata.dnd.settings
  238. *object* holds the global settings object for DND. You can easily modify any of the settings.
  239. >// modification example
  240. >$.vakata.dnd.settings.threshold = 10;
  241. */
  242. settings : {
  243. /*
  244. Variable: $.vakata.dnd.settings.scroll_speed
  245. *integer* how fast (pixel count for each step) should a scrollable parent scroll when dragging near the edge. Default is _10_.
  246. */
  247. scroll_speed : 10,
  248. /*
  249. Variable: $.vakata.dnd.settings.scroll_proximity
  250. *integer* number of pixels from the edge of a scrollable parent below which the parent will start scrolling. Default is _20_.
  251. */
  252. scroll_proximity : 20,
  253. /*
  254. Variable: $.vakata.dnd.settings.helper_left
  255. *integer* number of pixels left of the cursor to move the drag-helper to. Default is _5_;
  256. */
  257. helper_left : 5,
  258. /*
  259. Variable: $.vakata.dnd.settings.helper_top
  260. *integer* number of pixels below the cursor to move the drag-helper to. Default is _10_.
  261. */
  262. helper_top : 10,
  263. /*
  264. Variable: $.vakata.dnd.settings.threshold
  265. *integer* amount of pixels required to move before the drag is started. Default is _5_.
  266. */
  267. threshold : 5
  268. },
  269. /*
  270. Function: $.vakata.dnd._trigger
  271. Used internally to trigger all necessary events.
  272. */
  273. _trigger : function (event_name, e) {
  274. var data = $.vakata.dnd._get();
  275. data.event = e;
  276. $(document).triggerHandler("dnd_" + event_name + ".vakata", data);
  277. },
  278. /*
  279. Function: $.vakata.dnd._get
  280. Used internally to get all items for the drag event. Can be used by foreign code too.
  281. */
  282. _get : function () {
  283. return {
  284. "data" : vakata_dnd.data,
  285. "element" : vakata_dnd.element,
  286. "helper" : vakata_dnd.helper
  287. };
  288. },
  289. /*
  290. Function: $.vakata.dnd._clean
  291. Used internally to cleanup after a drop, so that all variables are nulled and ready for the next drag.
  292. */
  293. _clean : function () {
  294. if(vakata_dnd.helper) { vakata_dnd.helper.remove(); }
  295. if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }
  296. vakata_dnd = {
  297. element : false,
  298. is_down : false,
  299. is_drag : false,
  300. helper : false,
  301. helper_w: 0,
  302. data : false,
  303. init_x : 0,
  304. init_y : 0,
  305. scroll_l: 0,
  306. scroll_t: 0,
  307. scroll_e: false,
  308. scroll_i: false
  309. };
  310. $(document).unbind("mousemove", $.vakata.dnd.drag);
  311. $(document).unbind("mouseup", $.vakata.dnd.stop);
  312. },
  313. /*
  314. Function: $.vakata.dnd._scroll
  315. Used internally to scroll hovered elements.
  316. Triggers:
  317. <dnd_scroll>
  318. Event: dnd_scroll
  319. Fires when a container is scrolled due to dragging near its edge. Triggered on the document, the event is fired in the *vakata* namespace.
  320. Parameters:
  321. data.event - the scrolled element
  322. data.data - the data you supplied when calling <$.vakata.dnd.start>
  323. data.element - the origin element
  324. data.helper - the jquery extended drag-helper node (or false if it is not used)
  325. Example:
  326. >$(document).bind("dnd_start.vakata", function (e, data) {
  327. > // do something
  328. >});
  329. */
  330. _scroll : function (init_only) {
  331. if(!vakata_dnd.scroll_e || (!vakata_dnd.scroll_l && !vakata_dnd.scroll_t)) {
  332. if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }
  333. return false;
  334. }
  335. if(!vakata_dnd.scroll_i) {
  336. vakata_dnd.scroll_i = setInterval($.vakata.dnd._scroll, 100);
  337. return false;
  338. }
  339. if(init_only === true) { return false; }
  340. var i = vakata_dnd.scroll_e.scrollTop(),
  341. j = vakata_dnd.scroll_e.scrollLeft();
  342. vakata_dnd.scroll_e.scrollTop(i + vakata_dnd.scroll_t * $.vakata.dnd.settings.scroll_speed);
  343. vakata_dnd.scroll_e.scrollLeft(j + vakata_dnd.scroll_l * $.vakata.dnd.settings.scroll_speed);
  344. if(i !== vakata_dnd.scroll_e.scrollTop() || j !== vakata_dnd.scroll_e.scrollLeft()) {
  345. $.vakata.dnd._trigger("scroll", vakata_dnd.scroll_e);
  346. }
  347. },
  348. /*
  349. Function: $.vakata.dnd.start
  350. Use this function to start a drag (usually with the mousedown event)
  351. Parameters:
  352. event - *event* the event which started the drag, when used with the mousedown event text selection is prevented
  353. data - *mixed* some custom data you want to bind with that particular drag - you will receive this in all events
  354. html - *mixed* the text for the drag-helper as a *string*, if set to _false_ no helper is shown
  355. Returns:
  356. false
  357. Example:
  358. >$("span").bind("mousedown", function (e) {
  359. > return $.vakata.dnd.start(e, {}, "Dragging");
  360. >});
  361. */
  362. start : function (e, data, html) {
  363. if(vakata_dnd.is_drag) { $.vakata.dnd.stop({}); }
  364. try {
  365. e.currentTarget.unselectable = "on";
  366. e.currentTarget.onselectstart = function() { return false; };
  367. if(e.currentTarget.style) { e.currentTarget.style.MozUserSelect = "none"; }
  368. } catch(err) { }
  369. vakata_dnd.init_x = e.pageX;
  370. vakata_dnd.init_y = e.pageY;
  371. vakata_dnd.data = data;
  372. vakata_dnd.is_down = true;
  373. vakata_dnd.element = e.currentTarget;
  374. if(html !== false) {
  375. vakata_dnd.helper = $("<div id='vakata-dnd'></div>").html(html).css({
  376. "display" : "block",
  377. "margin" : "0",
  378. "padding" : "0",
  379. "position" : "absolute",
  380. "top" : "-2000px",
  381. "lineHeight" : "16px",
  382. "zIndex" : "10000"
  383. });
  384. }
  385. $(document).bind("mousemove", $.vakata.dnd.drag);
  386. $(document).bind("mouseup", $.vakata.dnd.stop);
  387. return false;
  388. },
  389. /*
  390. Function: $.vakata.dnd.drag
  391. Used internally to process the mousemove event after <$.vakata.dnd.start> is called.
  392. Parameters:
  393. event - *event* the mousemove event
  394. Triggers:
  395. <dnd_start>, <dnd_move>
  396. */
  397. drag : function (e) {
  398. if(!vakata_dnd.is_down) { return; }
  399. if(!vakata_dnd.is_drag) {
  400. if(
  401. Math.abs(e.pageX - vakata_dnd.init_x) > $.vakata.dnd.settings.threshold ||
  402. Math.abs(e.pageY - vakata_dnd.init_y) > $.vakata.dnd.settings.threshold
  403. ) {
  404. if(vakata_dnd.helper) {
  405. vakata_dnd.helper.appendTo("body");
  406. vakata_dnd.helper_w = vakata_dnd.helper.outerWidth();
  407. }
  408. vakata_dnd.is_drag = true;
  409. /*
  410. Event: dnd_start
  411. 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.
  412. Parameters:
  413. data.event - the mousemove event
  414. data.data - the data you supplied when calling <$.vakata.dnd.start>
  415. data.element - the origin element
  416. data.helper - the jquery extended drag-helper node (or false if it is not used)
  417. Example:
  418. >$(document).bind("dnd_start.vakata", function (e, data) {
  419. > // do something
  420. >});
  421. */
  422. $.vakata.dnd._trigger("start", e);
  423. }
  424. else { return; }
  425. }
  426. var d = false, w = false,
  427. dh = false, wh = false,
  428. dw = false, ww = false,
  429. dt = false, dl = false,
  430. ht = false, hl = false;
  431. vakata_dnd.scroll_t = 0;
  432. vakata_dnd.scroll_l = 0;
  433. vakata_dnd.scroll_e = false;
  434. var p = $(e.target)
  435. .parentsUntil("body").andSelf().vakata_reverse()
  436. .filter(function () {
  437. return (/^auto|scroll$/).test($(this).css("overflow")) &&
  438. (this.scrollHeight > this.offsetHeight || this.scrollWidth > this.offsetWidth);
  439. })
  440. .each(function () {
  441. var t = $(this), o = t.offset();
  442. if(this.scrollHeight > this.offsetHeight) {
  443. if(o.top + t.height() - e.pageY < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; scr = true; }
  444. if(e.pageY - o.top < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; scr = true; }
  445. }
  446. if(this.scrollWidth > this.offsetWidth) {
  447. if(o.left + t.width() - e.pageX < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; scr = true; }
  448. if(e.pageX - o.left < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; scr = true; }
  449. }
  450. if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {
  451. vakata_dnd.scroll_e = $(this);
  452. return false;
  453. }
  454. });
  455. if(!vakata_dnd.scroll_e) {
  456. d = $(document); w = $(window);
  457. dh = d.height(); wh = w.height();
  458. dw = d.width(); ww = w.width();
  459. dt = d.scrollTop(); dl = d.scrollLeft();
  460. if(dh > wh && e.pageY - dt < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; }
  461. if(dh > wh && wh - (e.pageY - dt) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; }
  462. if(dw > ww && e.pageX - dl < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; }
  463. if(dw > ww && ww - (e.pageX - dl) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; }
  464. if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {
  465. vakata_dnd.scroll_e = d;
  466. }
  467. }
  468. if(vakata_dnd.scroll_e) { $.vakata.dnd._scroll(true); }
  469. if(vakata_dnd.helper) {
  470. ht = parseInt(e.pageY + $.vakata.dnd.settings.helper_top, 10);
  471. hl = parseInt(e.pageX + $.vakata.dnd.settings.helper_left, 10);
  472. if(dh && ht + 25 > dh) { ht = dh - 50; }
  473. if(dw && hl + vakata_dnd.helper_w > dw) { hl = dw - (vakata_dnd.helper_w + 2); }
  474. vakata_dnd.helper.css({
  475. left : hl + "px",
  476. top : ht + "px"
  477. });
  478. }
  479. /*
  480. Event: dnd_move
  481. Triggered multiple times while dragging. This event is triggered on the document after the <dnd_start> event when the user moves the mouse, the event is fired in the *vakata* namespace.
  482. Parameters:
  483. data.event - the mousemove event
  484. data.data - the data you supplied when calling <$.vakata.dnd.start>
  485. data.element - the origin element
  486. data.helper - the jquery extended drag-helper node (or false if it is not used)
  487. Example:
  488. >$(document).bind("dnd_move.vakata", function (e, data) {
  489. > // do something
  490. >});
  491. */
  492. $.vakata.dnd._trigger("move", e);
  493. },
  494. /*
  495. Function: $.vakata.dnd.stop
  496. Used internally to process the mouseup event (drop) after <$.vakata.dnd.start> is called.
  497. Parameters:
  498. event - *event* the mouseup event
  499. Triggers:
  500. <dnd_stop>
  501. */
  502. stop : function (e) {
  503. /*
  504. Event: dnd_stop
  505. Marks the end of the drag. This event is triggered on the document after <dnd_start> (and possibly <dnd_move>) when a drop (mouseup) occurs or when the drag is programatically terminated, the event is fired in the *vakata* namespace.
  506. Parameters:
  507. data.event - the mouseup event (or _null_ if stopped programatically using <$.vakata.dnd.stop>())
  508. data.data - the data you supplied when calling <$.vakata.dnd.start>
  509. data.element - the origin element
  510. data.helper - the jquery extended drag-helper node (or false if it is not used)
  511. Example:
  512. >$(document).bind("dnd_stop.vakata", function (e, data) {
  513. > // do something
  514. >});
  515. */
  516. if(vakata_dnd.is_drag) {
  517. $.vakata.dnd._trigger("stop", e);
  518. }
  519. $.vakata.dnd._clean();
  520. }
  521. };
  522. })(jQuery);
  523. /*
  524. Group: XSLT
  525. A function used to do XSLT transformations.
  526. */
  527. (function ($) {
  528. /*
  529. Function: $.vakata.xslt
  530. This functions transforms a XML string using a XSL string. The result is passed to a callback function.
  531. Parameters:
  532. xml - *string* the source xml string
  533. xsl - *string* the xsl string
  534. Returns:
  535. the transformed result (or _false_ on failure)
  536. Example:
  537. >// simple
  538. >$.vakata.xslt("<xml-string-here>", "<xsl-string-here>", function (res) { $("#some-container").append(res); });
  539. >// with scope
  540. >$.vakata.xslt("<xml-string-here>", "<xsl-string-here>", $.proxy(function (res) {
  541. > this.some_process(res);
  542. >}, some_object);
  543. */
  544. $.vakata.xslt = function (xml, xsl) {
  545. var r = false, p, q, s;
  546. // IE9
  547. if(r === false && window.ActiveXObject) {
  548. try {
  549. r = new ActiveXObject("Msxml2.XSLTemplate");
  550. q = new ActiveXObject("Msxml2.DOMDocument");
  551. q.loadXML(xml);
  552. s = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");
  553. s.loadXML(xsl);
  554. r.stylesheet = s;
  555. p = r.createProcessor();
  556. p.input = q;
  557. p.transform();
  558. r = p.output;
  559. }
  560. catch (e) { }
  561. }
  562. xml = $.parseXML(xml);
  563. xsl = $.parseXML(xsl);
  564. // FF, Chrome
  565. if(r === false && typeof (XSLTProcessor) != "undefined") {
  566. p = new XSLTProcessor();
  567. p.importStylesheet(xsl);
  568. r = p.transformToFragment(xml, document);
  569. r = $('<div />').append(r).html();
  570. }
  571. // OLD IE
  572. if(r === false && typeof (xml.transformNode) != "undefined") {
  573. r = xml.transformNode(xsl);
  574. }
  575. return r;
  576. };
  577. })(jQuery);
  578. /*
  579. Group: Context menu
  580. Functions needed to show a custom context menu.
  581. */
  582. (function ($) {
  583. var right_to_left = false,
  584. vakata_context = {
  585. element : false,
  586. reference : false,
  587. position_x : 0,
  588. position_y : 0,
  589. items : [],
  590. html : "",
  591. is_visible : false
  592. };
  593. /*
  594. Variable: $.vakata.context
  595. *object* holds all context menu related functions and variables.
  596. */
  597. $.vakata.context = {
  598. /*
  599. Variable: $.vakata.context.settings
  600. *object* holds the global settings object for context menus. You can easily modify any of the settings.
  601. >// modification example
  602. >$.vakata.context.settings.icons = false;
  603. */
  604. settings : {
  605. /*
  606. Variable: $.vakata.context.settings.hide_onmouseleave
  607. *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_.
  608. */
  609. hide_onmouseleave : 0,
  610. /*
  611. Variable: $.vakata.context.settings.icons
  612. *boolean* whether to show icons or not. Default is _true_.
  613. */
  614. icons : true
  615. },
  616. /*
  617. Function: $.vakata.context._trigger
  618. Used internally to trigger all necessary events.
  619. */
  620. _trigger : function (event_name) {
  621. $(document).triggerHandler("context_" + event_name + ".vakata", {
  622. "reference" : vakata_context.reference,
  623. "element" : vakata_context.element,
  624. "position" : {
  625. "x" : vakata_context.position_x,
  626. "y" : vakata_context.position_y
  627. }
  628. });
  629. },
  630. /*
  631. Function: $.vakata.context._execute
  632. Used internally to execute the action (if any) associated with an item.
  633. Parameters:
  634. i - the item's internal index
  635. */
  636. _execute : function (i) {
  637. i = vakata_context.items[i];
  638. return i && i.action ? i.action.call(null, {
  639. "item" : i,
  640. "reference" : vakata_context.reference,
  641. "element" : vakata_context.element,
  642. "position" : {
  643. "x" : vakata_context.position_x,
  644. "y" : vakata_context.position_y
  645. }
  646. }) : false;
  647. },
  648. /*
  649. Function: $.vakata.context._parse
  650. Used internally to parse a contextmenu description object to an HTML string.
  651. Parameters:
  652. o - *object* the contextmenu description object
  653. is_callback - *boolean* used internally to indicate a recursive call
  654. Triggers:
  655. <context_parse>
  656. */
  657. _parse : function (o, is_callback) {
  658. if(!o) { return false; }
  659. if(!is_callback) {
  660. vakata_context.html = "";
  661. vakata_context.items = [];
  662. }
  663. var str = "",
  664. sep = false;
  665. if(is_callback) { str += "<ul>"; }
  666. $.each(o, function (i, val) {
  667. if(!val) { return true; }
  668. vakata_context.items.push(val);
  669. if(!sep && val.separator_before) {
  670. str += "<li class='vakata-context-separator'><a href='#' " + ($.vakata.context.settings.icons ? '' : 'style="margin-left:0px;"') + ">&#160;</a></li>";
  671. }
  672. sep = false;
  673. str += "<li class='" + (val._class || "") + (val._disabled ? " vakata-contextmenu-disabled " : "") + "'>";
  674. str += "<a href='#' rel='" + (vakata_context.items.length - 1) + "'>";
  675. if($.vakata.context.settings.icons) {
  676. str += "<ins ";
  677. if(val.icon) {
  678. if(val.icon.indexOf("/") !== -1) { str += " style='background:url(\"" + val.icon + "\") center center no-repeat' "; }
  679. else { str += " class='" + val.icon + "' "; }
  680. }
  681. str += ">&#160;</ins><span>&#160;</span>";
  682. }
  683. str += val.label + "</a>";
  684. if(val.submenu) {
  685. tmp = $.vakata.context._parse(val.submenu, true);
  686. if(tmp) { str += tmp; }
  687. }
  688. str += "</li>";
  689. if(val.separator_after) {
  690. str += "<li class='vakata-context-separator'><a href='#' " + ($.vakata.context.settings.icons ? '' : 'style="margin-left:0px;"') + ">&#160;</a></li>";
  691. sep = true;
  692. }
  693. });
  694. str = str.replace(/<li class\='vakata-context-separator'\><\/li\>$/,"");
  695. if(is_callback) { str += "</ul>"; }
  696. /*
  697. Event: context_parse
  698. Triggered when the context menu is parsed but not yet shown. This event is triggered on the document in the *vakata* namespace.
  699. Parameters:
  700. reference - the DOM node used when <$.vakata.context.show> was called
  701. element - the DOM node of the context menu (not yet populated and shown)
  702. position - an object consisting of _x_ and _y_ keys, represinting the position of the menu (not yet shown)
  703. Example:
  704. >$(document).bind("context_parse.vakata", function (e, data) {
  705. > // do something
  706. >});
  707. */
  708. if(!is_callback) { vakata_context.html = str; $.vakata.context._trigger("parse"); }
  709. return str.length > 10 ? str : false;
  710. },
  711. /*
  712. Function: $.vakata.context._show_submenu
  713. Used internally to show a submenu
  714. */
  715. _show_submenu : function (o) {
  716. o = $(o);
  717. if(!o.length || !o.children("ul").length) { return; }
  718. var e = o.children("ul"),
  719. x = o.offset().left + o.outerWidth(),
  720. y = o.offset().top,
  721. w = e.width(),
  722. h = e.height(),
  723. dw = $(document).width(),
  724. dh = $(document).height();
  725. // може да се спести е една проверка - дали няма някой от класовете вече нагоре
  726. if(right_to_left) {
  727. o[x - (w + 10 + o.outerWidth()) < 0 ? "addClass" : "removeClass"]("vakata-context-left");
  728. }
  729. else {
  730. o[x + w + 10 > dw ? "addClass" : "removeClass"]("vakata-context-right");
  731. }
  732. if(y + h + 10 > dh) {
  733. e.css("bottom","-1px");
  734. }
  735. e.show();
  736. },
  737. /*
  738. Function: $.vakata.context.show
  739. Shows the context menu. Please note that at least one of _reference_ or _position_ should be specified.
  740. Parameters:
  741. reference - *jquery* associate the menu with a DOM element (optional)
  742. position - *object* should contain _x_ and _y_ properties, those are the coordinates to show the menu at (optional
  743. data - *object* the contextmenu description object. It should consist of keys, each key should be a <context_menu_item>. If not specified the function will search for $(reference).data('vakata_contextmenu') and use that.
  744. Triggers:
  745. <context_show>
  746. Example:
  747. >$(document).bind("contextmenu", function (e) {
  748. > e.preventDefault();
  749. > $.vakata.context.show(false, { x: e.pageX, y:e.pageY }, {
  750. > "create" : {
  751. > // only specify what you need
  752. > "separator_after" : true,
  753. > "label" : "Create",
  754. > "action" : function (data) { alert("Create"); }
  755. > },
  756. > "rename" : {
  757. > "label" : "Rename",
  758. > "icon" : "./some-icon.png",
  759. > "action" : function (data) { alert("Rename on " + data.reference); }
  760. > },
  761. > "edit" : {
  762. > "label" : "Edit",
  763. > // Clicking this won't hide the menu, the same can be achieved with:
  764. > // "action" : function () { return false; }
  765. > "submenu" : {
  766. > "copy" : { "label" : "Copy", "action" : function () { } },
  767. > "cut" : { "label" : "Cut", "action" : function () { } },
  768. > "paste" : { "label" : "Paste", "_disabled" : true, "action" : function () { } }
  769. > }
  770. > },
  771. > "delete" : {
  772. > "separator_before" : true,
  773. > "label" : "Delete",
  774. > "action" : function (data) { alert("Delete"); }
  775. > }
  776. > });
  777. >});
  778. Variable: context_menu_item
  779. *object* Used to construct a context menu entry, this structure will always be a part of an object.
  780. separator_before - *boolean* should there be a separator before the item. Default is _false_.
  781. separator_after - *boolean* should there be a separator after the item. Default is _false_.
  782. 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.
  783. label - *string* the text for this item
  784. submenu - *object* if supplied this object is used to build a submenu. It should consist of keys, each of which is a <context_menu_item>.
  785. _class - *string* if supplied this class is applied to the LI node.
  786. _disabled - *boolean* is this item disabled.
  787. 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_.
  788. In the function you will receive a single argument which is an object, consisting of four keys:
  789. _item_ (the <context_menu_item> object),
  790. _reference_ (the DOM node used when <$.vakata.context.show> was called),
  791. _element_ (the DOM node of the context menu),
  792. _position_ (an object consisting of _x_ and _y_ keys, represinting the current position of the menu)
  793. See also:
  794. <$.vakata.context.show>
  795. */
  796. show : function (reference, position, data) {
  797. switch(!0) {
  798. case (!position && !reference):
  799. return false;
  800. case (!!position && !!reference):
  801. vakata_context.reference = reference;
  802. vakata_context.position_x = position.x;
  803. vakata_context.position_y = position.y;
  804. break;
  805. case (!position && !!reference):
  806. vakata_context.reference = reference;
  807. var o = reference.offset();
  808. vakata_context.position_x = o.left + reference.outerHeight();
  809. vakata_context.position_y = o.top;
  810. break;
  811. case (!!position && !reference):
  812. vakata_context.position_x = position.x;
  813. vakata_context.position_y = position.y;
  814. break;
  815. }
  816. if(!!reference && !data && $(reference).data('vakata_contextmenu')) {
  817. data = $(reference).data('vakata_contextmenu');
  818. }
  819. if($.vakata.context._parse(data)) {
  820. vakata_context.element.html(vakata_context.html);
  821. }
  822. if(vakata_context.items.length) {
  823. var e = vakata_context.element,
  824. x = vakata_context.position_x,
  825. y = vakata_context.position_y,
  826. w = e.width(),
  827. h = e.height(),
  828. dw = $(document).width(),
  829. dh = $(document).height();
  830. if(x + w + 20 > dw) {
  831. x = dw - (w + 20);
  832. }
  833. if(y + h + 20 > dh) {
  834. y = dh - (h + 20);
  835. }
  836. vakata_context.element
  837. .css({ "left" : x, "top" : y })
  838. .show()
  839. .width(vakata_context.element.outerWidth()); // for ie6
  840. vakata_context.is_visible = true;
  841. /*
  842. Event: context_show
  843. Triggered when the context menu is shown. This event is triggered on the document in the *vakata* namespace.
  844. Parameters:
  845. reference - the DOM node used when <$.vakata.context.show> was called
  846. element - the DOM node of the context menu
  847. position - an object consisting of _x_ and _y_ keys, represinting the position of the menu
  848. Example:
  849. >$(document).bind("context_show.vakata", function (e, data) {
  850. > // do something
  851. >});
  852. */
  853. $.vakata.context._trigger("show");
  854. }
  855. },
  856. /*
  857. Function: $.vakata.context.hide
  858. Used internally to hide the contextmenu after a click, or on mouseleave, etc.
  859. Triggers:
  860. <context_hide>
  861. */
  862. hide : function () {
  863. if(vakata_context.is_visible) {
  864. vakata_context.element.hide().find("ul").hide();
  865. vakata_context.is_visible = false;
  866. /*
  867. Event: context_hide
  868. Triggered when the context menu is hidden. This event is triggered on the document in the *vakata* namespace.
  869. Parameters:
  870. reference - the DOM node used when <$.vakata.context.show> was called
  871. element - the DOM node of the context menu
  872. position - an object consisting of _x_ and _y_ keys, represinting the position of the menu
  873. Example:
  874. >$(document).bind("context_hide.vakata", function (e, data) {
  875. > // do something
  876. >});
  877. */
  878. $.vakata.context._trigger("hide");
  879. }
  880. }
  881. };
  882. $(function () {
  883. right_to_left = $("body").css("direction") === "rtl";
  884. var to = false,
  885. css_string = '' +
  886. '.vakata-context { display:none; _width:1px; } ' +
  887. '.vakata-context, ' +
  888. '.vakata-context ul { margin:0; padding:2px; position:absolute; background:#f5f5f5; border:1px solid #979797; ' +
  889. ' -moz-box-shadow:5px 5px 4px -4px #666666; -webkit-box-shadow:2px 2px 2px #999999; box-shadow:2px 2px 2px #999999; }' +
  890. '.vakata-context ul { list-style:none; left:100%; margin-top:-2.7em; margin-left:-4px; } ' +
  891. '.vakata-context li.vakata-context-right ul { left:auto; right:100%; margin-left:auto; margin-right:-4px; } ' +
  892. '.vakata-context li { list-style:none; display:inline; }' +
  893. '.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; ' +
  894. ' -moz-text-shadow:1px 1px 0px white; -webkit-text-shadow:1px 1px 0px white; text-shadow:1px 1px 0px white; ' +
  895. ' -moz-border-radius:1px; -webkit-border-radius:1px; border-radius:1px; }' +
  896. '.vakata-context li a:hover { position:relative; background-color:#e8eff7; ' +
  897. ' -moz-box-shadow:0px 0px 2px #0a6aa1; -webkit-box-shadow:0px 0px 2px #0a6aa1; box-shadow:0px 0px 2px #0a6aa1; }' +
  898. '.vakata-context li.vakata-context-hover > a { position:relative; background-color:#e8eff7; ' +
  899. ' -moz-box-shadow:0px 0px 2px #0a6aa1; -webkit-box-shadow:0px 0px 2px #0a6aa1; box-shadow:0px 0px 2px #0a6aa1; }' +
  900. '.vakata-context li a.vakata-context-parent { background-image:url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAIORI4JlrqN1oMSnmmZDQUAOw=="); background-position:right center; background-repeat:no-repeat; } ' +
  901. '.vakata-context li.vakata-context-separator a, ' +
  902. '.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; ' +
  903. ' -moz-text-shadow:0 0 0 transparent; -webkit-text-shadow:0 0 0 transparent; text-shadow:0 0 0 transparent; ' +
  904. ' -moz-box-shadow:0 0 0 transparent; -webkit-box-shadow:0 0 0 transparent; box-shadow:0 0 0 transparent; ' +
  905. ' -moz-border-radius:0; -webkit-border-radius:0; border-radius:0; }' +
  906. '' +
  907. '.vakata-context li a ins { text-decoration:none; display:inline-block; width:2.4em; height:2.4em; background:transparent; margin:0 0 0 -2em; } ' +
  908. '.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; } ' +
  909. '' +
  910. '.vakata-context-rtl ul { left:auto; right:100%; margin-left:auto; margin-right:-4px; } ' +
  911. '.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; } ' +
  912. '.vakata-context-rtl li.vakata-context-separator a { margin:0 2.4em 0 0; border-left:0; border-right:1px solid #e2e3e3;} ' +
  913. '.vakata-context-rtl li.vakata-context-left ul { right:auto; left:100%; margin-left:-4px; margin-right:auto; } ' +
  914. '.vakata-context-rtl li a ins { margin:0 -2em 0 0; } ' +
  915. '.vakata-context-rtl li a span { margin:0 0 0 0.5em; border-left-color:white; background:#e2e3e3; } ' +
  916. '';
  917. $.vakata.css.add_sheet({ str : css_string, title : "vakata-context" });
  918. vakata_context.element = $("<ul class='vakata-context'></ul>");
  919. vakata_context.element
  920. .delegate("li", "mouseenter", function (e) {
  921. e.stopImmediatePropagation();
  922. if($.contains(this, e.relatedTarget)) {
  923. // премахнато заради delegate mouseleave по-долу
  924. // $(this).find(".vakata-context-hover").removeClass("vakata-context-hover");
  925. return;
  926. }
  927. if(to) { clearTimeout(to); }
  928. vakata_context.element.find(".vakata-context-hover").removeClass("vakata-context-hover").end();
  929. $(this)
  930. .siblings().find("ul").hide().end().end()
  931. .parentsUntil(".vakata-context", "li").andSelf().addClass("vakata-context-hover");
  932. $.vakata.context._show_submenu(this);
  933. })
  934. // тестово - дали не натоварва?
  935. .delegate("li", "mouseleave", function (e) {
  936. if($.contains(this, e.relatedTarget)) { return; }
  937. $(this).find(".vakata-context-hover").andSelf().removeClass("vakata-context-hover");
  938. })
  939. .bind("mouseleave", function (e) {
  940. $(this).find(".vakata-context-hover").removeClass("vakata-context-hover");
  941. if($.vakata.context.settings.hide_onmouseleave) {
  942. to = setTimeout(
  943. (function (t) {
  944. return function () { $.vakata.context.hide(); };
  945. })(this), $.vakata.context.settings.hide_onmouseleave);
  946. }
  947. })
  948. .delegate("a", "click", function (e) {
  949. e.preventDefault();
  950. })
  951. .delegate("a", "mouseup", function (e) {
  952. if(!$(this).blur().parent().hasClass("vakata-context-disabled") && $.vakata.context._execute($(this).attr("rel")) !== false) {
  953. $.vakata.context.hide();
  954. }
  955. })
  956. .appendTo("body");
  957. $(document)
  958. .bind("mousedown", function (e) {
  959. if(vakata_context.is_visible && !$.contains(vakata_context.element[0], e.target)) { $.vakata.context.hide(); }
  960. })
  961. .bind("context_show.vakata", function (e, data) {
  962. vakata_context.element.find("li:has(ul)").children("a").addClass("vakata-context-parent");
  963. if(right_to_left) {
  964. vakata_context.element.addClass("vakata-context-rtl").css("direction", "rtl");
  965. }
  966. // also apply a RTL class?
  967. vakata_context.element.find("ul").hide().end();
  968. });
  969. if(typeof $.hotkeys !== "undefined") {
  970. $(document)
  971. .bind("keydown", "up", function (e) {
  972. if(vakata_context.is_visible) {
  973. var o = vakata_context.element.find("ul:visible").andSelf().last().children(".vakata-context-hover").removeClass("vakata-context-hover").prevAll("li:not(.vakata-context-separator)").first();
  974. if(!o.length) { o = vakata_context.element.find("ul:visible").andSelf().last().children("li:not(.vakata-context-separator)").last(); }
  975. o.addClass("vakata-context-hover");
  976. e.stopImmediatePropagation();
  977. e.preventDefault();
  978. }
  979. })
  980. .bind("keydown", "down", function (e) {
  981. if(vakata_context.is_visible) {
  982. var o = vakata_context.element.find("ul:visible").andSelf().last().children(".vakata-context-hover").removeClass("vakata-context-hover").nextAll("li:not(.vakata-context-separator)").first();
  983. if(!o.length) { o = vakata_context.element.find("ul:visible").andSelf().last().children("li:not(.vakata-context-separator)").first(); }
  984. o.addClass("vakata-context-hover");
  985. e.stopImmediatePropagation();
  986. e.preventDefault();
  987. }
  988. })
  989. .bind("keydown", "right", function (e) {
  990. if(vakata_context.is_visible) {
  991. 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");
  992. e.stopImmediatePropagation();
  993. e.preventDefault();
  994. }
  995. })
  996. .bind("keydown", "left", function (e) {
  997. if(vakata_context.is_visible) {
  998. vakata_context.element.find(".vakata-context-hover").last().parents("li:eq(0)").find("ul").hide().find(".vakata-context-hover").removeClass("vakata-context-hover");
  999. e.stopImmediatePropagation();
  1000. e.preventDefault();
  1001. }
  1002. })
  1003. .bind("keydown", "esc", function (e) {
  1004. $.vakata.context.hide();
  1005. e.preventDefault();
  1006. })
  1007. .bind("keydown", "space", function (e) {
  1008. vakata_context.element.find(".vakata-context-hover").last().children("a").click();
  1009. e.preventDefault();
  1010. });
  1011. }
  1012. });
  1013. })(jQuery);
  1014. /*
  1015. Group: JSON
  1016. Functions needed to encode/decode JSON. Based on the jQuery JSON Plugin.
  1017. */
  1018. (function ($) {
  1019. // private function for quoting strings
  1020. var _quote = function (str) {
  1021. var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
  1022. meta = { '\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"' :'\\"','\\':'\\\\' };
  1023. if(str.match(escapeable)) {
  1024. return '"' + str.replace(escapeable, function (a) {
  1025. var c = _meta[a];
  1026. if(typeof c === 'string') { return c; }
  1027. c = a.charCodeAt();
  1028. return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
  1029. }) + '"';
  1030. }
  1031. return '"' + str + '"';
  1032. };
  1033. /*
  1034. Variable: $.vakata.json
  1035. *object* holds all JSON related functions.
  1036. */
  1037. $.vakata.json = {
  1038. /*
  1039. Function: $.vakata.json.encode
  1040. A function for encoding data in a JSON notated string.
  1041. Parameters:
  1042. o - *mixed* the data to be encoded
  1043. Returns:
  1044. string - the encoded data
  1045. */
  1046. encode : function (o) {
  1047. if (o === null) { return "null"; }
  1048. var tmp = [], i;
  1049. switch(typeof(o)) {
  1050. case "undefined":
  1051. return undefined;
  1052. case "number":
  1053. case "boolean":
  1054. return o + "";
  1055. case "string":
  1056. return _quote(o);
  1057. case "object":
  1058. if($.isFunction(o.toJSON)) {
  1059. return $.vakata.json.encode(o.toJSON());
  1060. }
  1061. if(o.constructor === Date) {
  1062. return '"' +
  1063. o.getUTCFullYear() + '-' +
  1064. String("0" + (o.getUTCMonth() + 1)).slice(-2) + '-' +
  1065. String("0" + o.getUTCDate()).slice(-2) + 'T' +
  1066. String("0" + o.getUTCHours()).slice(-2) + ':' +
  1067. String("0" + o.getUTCMinutes()).slice(-2) + ':' +
  1068. String("0" + o.getUTCSeconds()).slice(-2) + '.' +
  1069. String("00" + o.getUTCMilliseconds()).slice(-3) + 'Z"';
  1070. }
  1071. if(o.constructor === Array) {
  1072. for(i = 0; i < o.length; i++) {
  1073. tmp.push( $.vakata.json.encode(o[i]) || "null" );
  1074. }
  1075. return "[" + tmp.join(",") + "]";
  1076. }
  1077. $.each(o, function (i, v) {
  1078. if($.isFunction(v)) { return true; }
  1079. i = typeof i === "number" ? '"' + i + '"' : _quote(i);
  1080. v = $.vakata.json.encode(v);
  1081. tmp.push(i + ":" + v);
  1082. });
  1083. return "{" + tmp.join(", ") + "}";
  1084. }
  1085. },
  1086. /*
  1087. Function: $.vakata.json.decode
  1088. Exists for consistency and is a simple wrapper for jQuery.parseJSON.
  1089. Parameters:
  1090. json - the string to be decoded
  1091. Returns:
  1092. Same as jQuery.parseJSON
  1093. */
  1094. decode : function (json) {
  1095. return $.parseJSON(json);
  1096. }
  1097. };
  1098. })(jQuery);
  1099. /*
  1100. Group: Cookie
  1101. A copy of the jQuery cookie plugin.
  1102. */
  1103. (function ($) {
  1104. /*
  1105. Function: $.vakata.cookie
  1106. A function for getting and setting cookies.
  1107. Parameters:
  1108. Same as the original plugin
  1109. Returns:
  1110. string - the encoded data
  1111. */
  1112. $.vakata.cookie = function (key, value, options) {
  1113. var days, t, result, decode;
  1114. if (arguments.length > 1 && String(value) !== "[object Object]") {
  1115. options = $.extend({}, options);
  1116. if(value === null || value === undefined) { options.expires = -1; }
  1117. if(typeof options.expires === 'number') { days = options.expires; t = options.expires = new Date(); t.setDate(t.getDate() + days); }
  1118. value = String(value);
  1119. return (document.cookie = [
  1120. encodeURIComponent(key), '=',
  1121. options.raw ? value : encodeURIComponent(value),
  1122. options.expires ? '; expires=' + options.expires.toUTCString() : '',
  1123. options.path ? '; path=' + options.path : '',
  1124. options.domain ? '; domain=' + options.domain : '',
  1125. options.secure ? '; secure' : ''
  1126. ].join(''));
  1127. }
  1128. options = value || {};
  1129. decode = options.raw ? function (s) { return s; } : decodeURIComponent;
  1130. return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null;
  1131. };
  1132. })(jQuery);
  1133. /*
  1134. Group: LocalStorage
  1135. Functions for dealing with localStorage with fallback to userData or cookies. A slight modification of jstorage.
  1136. */
  1137. (function ($) {
  1138. var _storage = {},
  1139. _storage_service = {jStorage:"{}"},
  1140. _storage_elm = null,
  1141. _storage_size = 0,
  1142. json_encode = $.vakata.json.encode,
  1143. json_decode = $.vakata.json.decode,
  1144. _backend = false;
  1145. function _init() {
  1146. if("localStorage" in window) {
  1147. try {
  1148. if(window.localStorage) {
  1149. _storage_service = window.localStorage;
  1150. _backend = "localStorage";
  1151. }
  1152. } catch(E3) {/* Firefox fails when touching localStorage and cookies are disabled */}
  1153. }
  1154. else if("globalStorage" in window) {
  1155. try {
  1156. if(window.globalStorage) {
  1157. _storage_service = window.globalStorage[window.location.hostname];
  1158. _backend = "globalStorage";
  1159. }
  1160. } catch(E4) {/* Firefox fails when touching localStorage and cookies are disabled */}
  1161. }
  1162. else {
  1163. _storage_elm = document.createElement('link');
  1164. if(_storage_elm.addBehavior) {
  1165. _storage_elm.style.behavior = 'url(#default#userData)';
  1166. document.getElementsByTagName('head')[0].appendChild(_storage_elm);
  1167. alert(_storage_elm.load);
  1168. _storage_elm.load("jStorage");
  1169. var data = "{}";
  1170. try {
  1171. data = _storage_elm.getAttribute("jStorage");
  1172. } catch(E5) {}
  1173. _storage_service.jStorage = data;
  1174. _backend = "userDataBehavior";
  1175. }
  1176. else if(
  1177. !!$.vakata.cookie('vjstorage') ||
  1178. ($.vakata.cookie('vjstorage', '{}', { 'expires' : 365 }) && $.vakata.cookie('vjstorage') === '{}')
  1179. ) {
  1180. _storage_elm = null;
  1181. _storage_service.jStorage = $.vakata.cookie('vjstorage');
  1182. _backend = "cookie";
  1183. }
  1184. else {
  1185. _storage_elm = null;
  1186. return;
  1187. }
  1188. }
  1189. _load_storage();
  1190. }
  1191. function _load_storage() {
  1192. if(_storage_service.jStorage) {
  1193. try {
  1194. _storage = json_decode(String(_storage_service.jStorage));
  1195. } catch(E6) { _storage_service.jStorage = "{}"; }
  1196. } else {
  1197. _storage_service.jStorage = "{}";
  1198. }
  1199. _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;
  1200. }
  1201. function _save() {
  1202. try {
  1203. _storage_service.jStorage = json_encode(_storage);
  1204. if(_backend === 'userDataBehavior') {
  1205. _storage_elm.setAttribute("jStorage", _storage_service.jStorage);
  1206. _storage_elm.save("jStorage");
  1207. }
  1208. if(_backend === 'cookie') {
  1209. $.vakata.cookie('vjstorage', _storage_service.jStorage, { 'expires' : 365 });
  1210. }
  1211. _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
  1212. } catch(E7) { /* probably cache is full, nothing is saved this way*/ }
  1213. }
  1214. function _checkKey(key) {
  1215. if(!key || (typeof key != "string" && typeof key != "number")){
  1216. throw new TypeError('Key name must be string or numeric');
  1217. }
  1218. return true;
  1219. }
  1220. /*
  1221. Variable: $.vakata.storage
  1222. *object* holds all storage related functions and properties.
  1223. */
  1224. $.vakata.storage = {
  1225. /*
  1226. Variable: $.vakata.storage.version
  1227. *string* the version of jstorage used
  1228. */
  1229. version: "0.1.5.2",
  1230. /*
  1231. Function: $.vakata.storage.set
  1232. Set a key to a value
  1233. Parameters:
  1234. key - the key
  1235. value - the value
  1236. Returns:
  1237. _value_
  1238. */
  1239. set : function (key, value) {
  1240. _checkKey(key);
  1241. _storage[key] = value;
  1242. _save();
  1243. return value;
  1244. },
  1245. /*
  1246. Function: $.vakata.storage.get
  1247. Get a value by key.
  1248. Parameters:
  1249. key - the key
  1250. def - the value to return if _key_ is not found
  1251. Returns:
  1252. The found value, _def_ if key not found or _null_ if _def_ is not supplied.
  1253. */
  1254. get : function (key, def) {
  1255. _checkKey(key);
  1256. if(key in _storage){
  1257. return _storage[key];
  1258. }
  1259. return typeof(def) == 'undefined' ? null : def;
  1260. },
  1261. /*
  1262. Function: $.vakata.storage.del
  1263. Remove a key.
  1264. Parameters:
  1265. key - the key
  1266. Returns:
  1267. *boolean*
  1268. */
  1269. del : function (key) {
  1270. _checkKey(key);
  1271. if(key in _storage) {
  1272. delete _storage[key];
  1273. _save();
  1274. return true;
  1275. }
  1276. return false;
  1277. },
  1278. /*
  1279. Function: $.vakata.storage.flush
  1280. Empty the storage.
  1281. Returns:
  1282. _true_
  1283. */
  1284. flush : function(){
  1285. _storage = {};
  1286. _save();
  1287. // try{ window.localStorage.clear(); } catch(E8) { }
  1288. return true;
  1289. },
  1290. /*
  1291. Function: $.vakata.storage.storageObj
  1292. Get a read only copy of the whole storage.
  1293. Returns:
  1294. *object*
  1295. */
  1296. storageObj : function(){
  1297. function F() {}
  1298. F.prototype = _storage;
  1299. return new F();
  1300. },
  1301. /*
  1302. Function: $.vakata.storage.index
  1303. Get an array of all the set keys in the storage.
  1304. Returns:
  1305. *array*
  1306. */
  1307. index : function(){
  1308. var index = [], i;
  1309. $.each(_storage, function (i, v) { index.push(i); });
  1310. return index;
  1311. },
  1312. /*
  1313. Function: $.vakata.storage.storageSize
  1314. Get the size of all items in the storage in bytes.
  1315. Returns:
  1316. *number*
  1317. */
  1318. storageSize : function(){
  1319. return _storage_size;
  1320. },
  1321. /*
  1322. Function: $.vakata.storage.currentBackend
  1323. Get the current backend used.
  1324. Returns:
  1325. *string*
  1326. */
  1327. currentBackend : function(){
  1328. return _backend;
  1329. },
  1330. /*
  1331. Function: $.vakata.storage.currentBackend
  1332. See if storage functionality is available.
  1333. Returns:
  1334. *boolean*
  1335. */
  1336. storageAvailable : function(){
  1337. return !!_backend;
  1338. }
  1339. };
  1340. _init();
  1341. })(jQuery);
  1342. /*
  1343. Group: PrettyDate
  1344. Modifies time elements to a more human readable value. Taken from: https://github.com/zachleat/Humane-Dates/blob/master/src/humane.js
  1345. */
  1346. (function ($) {
  1347. /*
  1348. Variable: $.vakata.pretty_date
  1349. *object* holds all pretty-date related functions and properties.
  1350. */
  1351. $.vakata.pretty_date = {
  1352. /*
  1353. Variable: $.vakata.pretty_date.lang
  1354. *object* the localization to use.
  1355. */
  1356. lang : {
  1357. ago: 'Ago',
  1358. from: 'From Now',
  1359. now: 'Just Now',
  1360. minute: 'Minute',
  1361. minutes: 'Minutes',
  1362. hour: 'Hour',
  1363. hours: 'Hours',
  1364. day: 'Day',
  1365. days: 'Days',
  1366. week: 'Week',
  1367. weeks: 'Weeks',
  1368. month: 'Month',
  1369. months: 'Months',
  1370. year: 'Year',
  1371. years: 'Years'
  1372. },
  1373. /*
  1374. Function: $.vakata.pretty_date.parse
  1375. Parses the difference between to dates to a human readable string.
  1376. Parameters:
  1377. date - the date to calculate from (а string in this YYYY-MM-DDTHH:MM:SSZ format - UTC)
  1378. comareTo - the date to compare to (as date), if left empty the current date is used
  1379. Returns:
  1380. *mixed* - the formatted string on success or _null_ on error
  1381. */
  1382. parse : function (date, compareTo) {
  1383. // remove the timezone (always use gmdate on server side)
  1384. date = new Date(date.replace(/-/g,"/").replace(/[TZ]/g," ").replace(/\+\d\d\:\d\d$/,''));
  1385. compareTo = compareTo || new Date();
  1386. var lang = $.vakata.pretty_date.lang,
  1387. formats = [
  1388. [60, lang.now],
  1389. [3600, lang.minute, lang.minutes, 60], // 60 minutes, 1 minute
  1390. [86400, lang.hour, lang.hours, 3600], // 24 hours, 1 hour
  1391. [604800, lang.day, lang.days, 86400], // 7 days, 1 day
  1392. [2628000, lang.week, lang.weeks, 604800], // ~1 month, 1 week
  1393. [31536000, lang.month, lang.months, 2628000], // 1 year, ~1 month
  1394. [Infinity, lang.year, lang.years, 31536000] // Infinity, 1 year
  1395. ],
  1396. seconds = (compareTo - date + compareTo.getTimezoneOffset() * 60000) / 1000,
  1397. normalize = function (val, single) {
  1398. var margin = 0.1;
  1399. if(val >= single && val <= single * (1+margin)) {
  1400. return single;
  1401. }
  1402. return val;
  1403. },
  1404. token;
  1405. if(seconds < 0) {
  1406. seconds = Math.abs(seconds);
  1407. token = ' ' + lang.from;
  1408. }
  1409. else {
  1410. token = ' ' + lang.ago;
  1411. }
  1412. for(var i = 0, format = formats[0]; formats[i]; format = formats[++i]) {
  1413. if(seconds < format[0]) {
  1414. if(i === 0) {
  1415. return format[1];
  1416. }
  1417. var val