PageRenderTime 57ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/js/jQuery.radmenu.js

https://bitbucket.org/gridinoc/bibliotype
JavaScript | 350 lines | 203 code | 17 blank | 130 comment | 23 complexity | 01a348ee41a8ffcf6d83f788a2d1dfd3 MD5 | raw file
  1. /*!
  2. * jQuery Radmenu (Radial Menu) Plugin
  3. * version: 0.9.8 (11-SEPT-2010)
  4. * @requires v1.3.2 or later
  5. *
  6. * Documentation:
  7. * http://www.tikku.com/jquery-radmenu-plugin
  8. *
  9. * Copyright 2010, Nirvana Tikku (ntikku@gmail.com)
  10. *
  11. * Dual licensed under the MIT and GPL licenses:
  12. * http://www.opensource.org/licenses/mit-license.php
  13. * http://www.gnu.org/licenses/gpl.html
  14. */
  15. ;(function($){
  16. // radmenu namespace
  17. var RADMENU = ".radmenu", // events are radmenu.{event} - guarantee no NS collision
  18. OPTS = "options"+RADMENU,
  19. PREVOPTS = "prevoptions"+RADMENU;
  20. // private :: defaults
  21. var defaults = {
  22. listClass: "list",
  23. itemClass: "item",
  24. activeItemClass: "active",
  25. selectEvent: null, // click, mouseenter etc
  26. onSelect: function($selected){},
  27. radius: 10, // in pixels
  28. initialScale: 1,
  29. angleOffset: 0, // in radians
  30. centerX: 0,
  31. centerY: 0,
  32. animSpeed: 500,
  33. scaleAnimSpeed: 400,
  34. scaleAnimOpts: {},
  35. afterAnimation: function($m){},
  36. onShow: function($items){$items.show();},
  37. onHide: function($items){$items.hide();},
  38. // example onScaleItem: $item.css("font-size", factor+"em");
  39. onScaleItem: function($item, factor, coords){}
  40. };
  41. // DEFAULTS
  42. $.radmenu = {
  43. container: {
  44. clz: "radial_div",
  45. itemClz: "radial_div_item",
  46. html: "<div></div>",
  47. css: { "position": "relative" }
  48. }
  49. };
  50. /**
  51. * jQuery Radmenu Plugin
  52. * @params
  53. * > input, dealt with by type
  54. * if empty - assumes initialization
  55. * if object - assumes initialization
  56. * if string - assumes trigger method
  57. * if number - select a particular menu item
  58. */
  59. $.fn.radmenu = function(input, param){
  60. try {
  61. var $this = $(this);
  62. var type = typeof input;
  63. if(arguments.length==0 || type=="object")
  64. return init($this, input);
  65. else if(type=="string")
  66. return input=="items" ?
  67. $this.triggerHandler(input+RADMENU) :
  68. $this.trigger(input+RADMENU, param||null);
  69. else if(type=="number"){
  70. return $this.trigger("select"+RADMENU,input);
  71. }
  72. } catch (e){ return "error : "+e; }
  73. };
  74. /**
  75. * private :: init fn
  76. * @params
  77. * $menu - the jQuery obj / array w/ menu target
  78. * opts - options object, to be merged with defaults
  79. */
  80. function init($menu, opts){
  81. var o = $.extend({}, defaults, opts);
  82. return $menu.each(function(m){
  83. var $this = $(this);
  84. var $list = $this.find("."+o.listClass);
  85. $list.find("."+o.itemClass).hide(); // ensure its hidden
  86. // set the options within the data for the elem & bind evts
  87. $this.data(OPTS, updateRadius(o, o.initialScale, o.radius));
  88. for(e in MENU) $this.bind(e+RADMENU, $this, MENU[e]);
  89. });
  90. };
  91. /**
  92. * selectMenuitem
  93. * @param
  94. * evt - the event object
  95. * triggers select event on radmenu container
  96. * using the index of the 'target object'
  97. */
  98. function selectMenuitem(evt){
  99. var $this = $(this);
  100. var $element = $(evt.target);
  101. var container = $.radmenu.container;
  102. if(!$element.hasClass(container.itemClz))
  103. $element = $element.closest("."+container.itemClz);
  104. var isInNested = $element.parents("."+container.itemClz).length>0;
  105. var index = $element.index();
  106. if(!isInNested)$this.parents("."+container.clz).radmenu(index);
  107. else $this.radmenu(index);
  108. cancelBubble(evt);
  109. };
  110. /**
  111. * cancel event bubbling - x-browser friendly
  112. * @param
  113. * evt - the event object
  114. */
  115. function cancelBubble(evt){
  116. if(!$.support.opacity) window.event.cancelBubble = true;
  117. else evt.stopPropagation();
  118. };
  119. /**
  120. * All the MENU events to be bound to the radial menu
  121. */
  122. var MENU = {
  123. show: function(evt, fn){ // fn = user input onshow
  124. var $m = getMenu(evt);
  125. var container = $.radmenu.container;
  126. // clear any existing radial menus within the menu
  127. $m.menu.find("."+container.clz).remove();
  128. // grab the desired menu items to be used in building the radmenu
  129. var $menuitems = $m.menu.find("."+$m.opts.itemClass);
  130. // create a div that will be the radmenu & create the HTML for the items
  131. var $radialMenu = $(container.html)
  132. .addClass(container.clz).css(container.css)
  133. .html(buildMenuHTML($menuitems, $m.opts));
  134. // assign a selection event if the user has specified something
  135. var $menuitems = $radialMenu.find("."+container.itemClz);
  136. if($m.opts.selectEvent!=null)
  137. $menuitems.bind($m.opts.selectEvent,selectMenuitem);
  138. // append the radmenu items inside the menu
  139. $radialMenu.appendTo($m.menu);
  140. if(typeof(fn) == "function") fn($menuitems);
  141. else $m.opts.onShow($menuitems); // user can do what they want
  142. cancelBubble(evt);
  143. },
  144. hide: function(evt){
  145. var $m = getMenu(evt);
  146. // remove the radmenu that was built and appended inside the menu
  147. var $menu = $m.menu.find("."+$.radmenu.container.clz);
  148. $m.opts.onHide($menu.find("."+$.radmenu.container.itemClz));
  149. $menu.remove();
  150. cancelBubble(evt);
  151. },
  152. select: function(evt, selectIndex){
  153. var $m = getMenu(evt);
  154. // with a specific index specified, grab the item
  155. var $selected = $($m.raditems().get(selectIndex));
  156. // remove the active class on the elements siblings
  157. $selected.siblings().removeClass($m.opts.activeItemClass);
  158. // add the active class on the selected item
  159. $selected.addClass($m.opts.activeItemClass);
  160. // pass the selected item to a customizable function
  161. $m.opts.onSelect($selected);
  162. cancelBubble(evt);
  163. },
  164. next: function(evt){ // clockwise
  165. var $m = getMenu(evt);
  166. // switch the first and last items and then animate
  167. switchItems($m, $m.raditems().length-1, 0, 1);
  168. },
  169. prev: function(evt){ // anticlockwise
  170. var $m = getMenu(evt);
  171. // switch the last and first items and then animate
  172. switchItems($m, 0, $m.raditems().length-1, 1);
  173. },
  174. shuffle: function(evt,rndOffset){
  175. var $m = getMenu(evt);
  176. var len = $m.raditems().length;
  177. // swap some random item with another random item, and add some shuffling effects
  178. switchItems($m, rnd(len), rnd(len), rnd(rndOffset||15));
  179. },
  180. destroy: function(evt){
  181. var $m = getMenu(evt);
  182. $m.menu.data(OPTS, null).data(PREVOPTS, null).unbind(RADMENU);
  183. return $m.menu;
  184. },
  185. items: function(evt){return getMenu(evt).raditems();},
  186. scale: function(evt, factor){
  187. var $m = getMenu(evt);
  188. if(factor){
  189. var o = $m.opts;
  190. var container = $.radmenu.container;
  191. var prevOpts = $m.menu.data(PREVOPTS);
  192. if(!prevOpts) $m.menu.data(PREVOPTS, prevOpts=o);
  193. // get the radial menu items
  194. var $items = $m.menu.find("."+container.itemClz);
  195. var updatedRadiusOpts = updateRadius(o, factor, prevOpts.radius);
  196. $m.menu.data(OPTS, updatedRadiusOpts); // save the radius for anim purposes
  197. $items.each(function(i){ // for each item update the x,y + css
  198. var $this = $(this);
  199. var coords = getCoords(i+1, $items.length, updatedRadiusOpts);
  200. var animOpts = {left: coords.x,top: coords.y};
  201. if(typeof(o.scaleAnimOpts) == "object") {
  202. animOpts = $.extend({}, o.scaleAnimOpts, {left: coords.x,top: coords.y});
  203. }
  204. $this.animate(animOpts, o.scaleAnimSpeed);
  205. $m.opts.onScaleItem($this, factor, coords);
  206. });
  207. }
  208. return $m.menu;
  209. }
  210. };
  211. function updateRadius(opts, radius, factor){
  212. return $.extend({},opts,{radius:(factor*radius)});
  213. };
  214. // random int offset
  215. function rnd(i){return parseInt(Math.random()*i);};
  216. /**
  217. * getMenu
  218. * @params
  219. * evt - the event object
  220. * @return
  221. * Object
  222. * > menu - jQueryfied menu
  223. * > opts - the options
  224. * > raditems - the radial menu items
  225. */
  226. function getMenu(evt){
  227. var $menu = evt.data;
  228. return {
  229. menu: $menu,
  230. opts: $menu.data(OPTS),
  231. raditems: function(){
  232. // you will want to trigger raditems() if the contents get modified
  233. return $menu.find("."+$.radmenu.container.itemClz);
  234. }
  235. };
  236. };
  237. /**
  238. * switchItems
  239. * @params
  240. * $m - the menu package
  241. * remove - the index of the menuitem to replace in the swap
  242. * add - the index of the menuitem to use in the swap (a placeholder)
  243. */
  244. function switchItems($m, remove, add, posOffset){
  245. if(remove==add) add = remove - 1; // ensure that we don't lose any items
  246. var $remove = $($m.raditems()[remove]); // grab the replacement item
  247. var toAddto = $m.raditems()[add]; // grab the placeholder
  248. // insertion is dependent on index of items
  249. if(remove>add) $remove.insertBefore(toAddto);
  250. else $remove.insertAfter(toAddto);
  251. animateWheel($m,posOffset); // posOffset = 5:neat, 10:fireworksesque, 15:subtleish
  252. };
  253. /**
  254. * buildMenuHTML - returns string instead of objects
  255. * for performance
  256. * @params
  257. * $menuitems - the jQueryified menu items
  258. * opts - the radial menu's options
  259. * @return
  260. * String
  261. * > each item is wrapped with an
  262. * absolute positioned div at an
  263. * offset determined by it's location
  264. * on a circle
  265. */
  266. function buildMenuHTML($menuitems, opts){
  267. var ret = "";
  268. $menuitems.each(function(i){ // for each item we will want to build the HTML
  269. var $this = $(this);
  270. var coords = getCoords(i+1, $menuitems.length, opts); // each item has a position
  271. ret += "<div class='"+$.radmenu.container.itemClz+"' "; // outer container for the div
  272. // after getting the coordinates, absolute position element at (x,y)
  273. ret += "style='position:absolute;left:"+coords.x+"px;top:"+coords.y+"px;display:none;'>";
  274. ret += $this.html(); // append the HTML _within_ the user's defined 'item'
  275. ret += "</div>";
  276. });
  277. return ret;
  278. };
  279. /**
  280. * getCoords - returns coordinates for menuitems
  281. * @params
  282. * idx - the instance index (1st, 2nd, 3rd, etc..)
  283. * num - the number of menuitems to spread
  284. * opts - the options provided by the user customizations
  285. * @return
  286. * Object - (x, y) coords
  287. */
  288. function getCoords(idx, num, opts){
  289. var radius = opts.radius; // user specified radius
  290. var angleOffset = opts.angleOffset; // provide flexibility of angle
  291. var angle = 2 * Math.PI * (parseFloat(idx/num)); // radians
  292. // assuming: hypotenuse (hyp) = radius
  293. //
  294. // opposite |\ hypotenuse
  295. // | \
  296. // 90deg |__\ (*theta* - angle)
  297. // adjacent
  298. //
  299. // x-axis offset: cos(theta) = adjacent / hypotenuse
  300. // ==> adjacent = left = cos(theta) * radius
  301. // y-axis offset: sin(theta) = opposite / hypotenuse
  302. // ==> opposite = top = sin(theta) * radius
  303. var l = opts.centerX + (Math.cos(angle + angleOffset) * radius), // "left"
  304. t = opts.centerY + (Math.sin(angle + angleOffset) * radius); // "top"
  305. return {x: l, y: t}; // return the x,y coords
  306. };
  307. /**
  308. * animateWheel - performs animation
  309. * @params
  310. * $m - object holding menu & options
  311. * posOffset - the position offset for the initial menuitem
  312. */
  313. function animateWheel($m, posOffset){
  314. // get the menu from the $m menu package
  315. var $menuitems = $m.menu.find("."+$.radmenu.container.itemClz);
  316. // get a handle on the number of items
  317. var len = $menuitems.length;
  318. // for each item, we're going to animate left/top attributes
  319. $menuitems.each(function(i){
  320. var $this = $(this);
  321. // establish the new coordinates with a customizable offset; len*(Math.PI+(Math.sqrt(5)))
  322. var coords = getCoords(i+posOffset, len, $m.opts);
  323. // playing with this is fun - this basically just
  324. // performs the animation with new coordinates
  325. $this.animate({
  326. left: coords.x, top: coords.y
  327. }, $m.opts.animSpeed, i==(len-1)?function(){
  328. // allow the user to do something after completing an animation
  329. $m.opts.afterAnimation($m);
  330. }:undefined);
  331. });
  332. };
  333. })(jQuery);