[ Index ]

PHP Cross Reference of vtigercrm-6.1.0

title

Body

[close]

/libraries/jquery/handsontable/jQuery-contextMenu/ -> jquery.contextMenu.js (source)

   1  /*!
   2   * jQuery contextMenu - Plugin for simple contextMenu handling
   3   *
   4   * Version: 1.5.25
   5   *
   6   * Authors: Rodney Rehm, Addy Osmani (patches for FF)
   7   * Web: http://medialize.github.com/jQuery-contextMenu/
   8   *
   9   * Licensed under
  10   *   MIT License http://www.opensource.org/licenses/mit-license
  11   *   GPL v3 http://opensource.org/licenses/GPL-3.0
  12   *
  13   */
  14  
  15  (function($, undefined){
  16      
  17      // TODO: -
  18          // ARIA stuff: menuitem, menuitemcheckbox und menuitemradio
  19          // create <menu> structure if $.support[htmlCommand || htmlMenuitem] and !opt.disableNative
  20  
  21  // determine html5 compatibility
  22  $.support.htmlMenuitem = ('HTMLMenuItemElement' in window);
  23  $.support.htmlCommand = ('HTMLCommandElement' in window);
  24  $.support.eventSelectstart = ("onselectstart" in document.documentElement);
  25  /* // should the need arise, test for css user-select
  26  $.support.cssUserSelect = (function(){
  27      var t = false,
  28          e = document.createElement('div');
  29      
  30      $.each('Moz|Webkit|Khtml|O|ms|Icab|'.split('|'), function(i, prefix) {
  31          var propCC = prefix + (prefix ? 'U' : 'u') + 'serSelect',
  32              prop = (prefix ? ('-' + prefix.toLowerCase() + '-') : '') + 'user-select';
  33              
  34          e.style.cssText = prop + ': text;';
  35          if (e.style[propCC] == 'text') {
  36              t = true;
  37              return false;
  38          }
  39          
  40          return true;
  41      });
  42      
  43      return t;
  44  })();
  45  */
  46  
  47  var // currently active contextMenu trigger
  48      $currentTrigger = null,
  49      // is contextMenu initialized with at least one menu?
  50      initialized = false,
  51      // window handle
  52      $win = $(window),
  53      // number of registered menus
  54      counter = 0,
  55      // mapping selector to namespace
  56      namespaces = {},
  57      // mapping namespace to options
  58      menus = {},
  59      // custom command type handlers
  60      types = {},
  61      // default values
  62      defaults = {
  63          // selector of contextMenu trigger
  64          selector: null,
  65          // where to append the menu to
  66          appendTo: null,
  67          // method to trigger context menu ["right", "left", "hover"]
  68          trigger: "right",
  69          // hide menu when mouse leaves trigger / menu elements
  70          autoHide: false,
  71          // ms to wait before showing a hover-triggered context menu
  72          delay: 200,
  73          // determine position to show menu at
  74          determinePosition: function($menu) {
  75              // position to the lower middle of the trigger element
  76              if ($.ui && $.ui.position) {
  77                  // .position() is provided as a jQuery UI utility
  78                  // (...and it won't work on hidden elements)
  79                  $menu.css('display', 'block').position({
  80                      my: "center top",
  81                      at: "center bottom",
  82                      of: this,
  83                      offset: "0 5",
  84                      collision: "fit"
  85                  }).css('display', 'none');
  86              } else {
  87                  // determine contextMenu position
  88                  var offset = this.offset();
  89                  offset.top += this.outerHeight();
  90                  offset.left += this.outerWidth() / 2 - $menu.outerWidth() / 2;
  91                  $menu.css(offset);
  92              }
  93          },
  94          // position menu
  95          position: function(opt, x, y) {
  96              var $this = this,
  97                  offset;
  98              // determine contextMenu position
  99              if (!x && !y) {
 100                  opt.determinePosition.call(this, opt.$menu);
 101                  return;
 102              } else if (x === "maintain" && y === "maintain") {
 103                  // x and y must not be changed (after re-show on command click)
 104                  offset = opt.$menu.position();
 105              } else {
 106                  // x and y are given (by mouse event)
 107                  var triggerIsFixed = opt.$trigger.parents().andSelf()
 108                      .filter(function() {
 109                          return $(this).css('position') == "fixed";
 110                      }).length;
 111  
 112                  if (triggerIsFixed) {
 113                      y -= $win.scrollTop();
 114                      x -= $win.scrollLeft();
 115                  }
 116                  offset = {top: y, left: x};
 117              }
 118              
 119              // correct offset if viewport demands it
 120              var bottom = $win.scrollTop() + $win.height(),
 121                  right = $win.scrollLeft() + $win.width(),
 122                  height = opt.$menu.height(),
 123                  width = opt.$menu.width();
 124              
 125              if (offset.top + height > bottom) {
 126                  offset.top -= height;
 127              }
 128              
 129              if (offset.left + width > right) {
 130                  offset.left -= width;
 131              }
 132              
 133              opt.$menu.css(offset);
 134          },
 135          // position the sub-menu
 136          positionSubmenu: function($menu) {
 137              if ($.ui && $.ui.position) {
 138                  // .position() is provided as a jQuery UI utility
 139                  // (...and it won't work on hidden elements)
 140                  $menu.css('display', 'block').position({
 141                      my: "left top",
 142                      at: "right top",
 143                      of: this,
 144                      collision: "fit"
 145                  }).css('display', '');
 146              } else {
 147                  // determine contextMenu position
 148                  var offset = {
 149                      top: 0,
 150                      left: this.outerWidth()
 151                  };
 152                  $menu.css(offset);
 153              }
 154          },
 155          // offset to add to zIndex
 156          zIndex: 1,
 157          // show hide animation settings
 158          animation: {
 159              duration: 50,
 160              show: 'slideDown',
 161              hide: 'slideUp'
 162          },
 163          // events
 164          events: {
 165              show: $.noop,
 166              hide: $.noop
 167          },
 168          // default callback
 169          callback: null,
 170          // list of contextMenu items
 171          items: {}
 172      },
 173      // mouse position for hover activation
 174      hoveract = {
 175          timer: null,
 176          pageX: null,
 177          pageY: null
 178      },
 179      // determine zIndex
 180      zindex = function($t) {
 181          var zin = 0,
 182              $tt = $t;
 183  
 184          while (true) {
 185              zin = Math.max(zin, parseInt($tt.css('z-index'), 10) || 0);
 186              $tt = $tt.parent();
 187              if (!$tt || !$tt.length || "html body".indexOf($tt.prop('nodeName').toLowerCase()) > -1 ) {
 188                  break;
 189              }
 190          }
 191          
 192          return zin;
 193      },
 194      // event handlers
 195      handle = {
 196          // abort anything
 197          abortevent: function(e){
 198              e.preventDefault();
 199              e.stopImmediatePropagation();
 200          },
 201          
 202          // contextmenu show dispatcher
 203          contextmenu: function(e) {
 204              var $this = $(this);
 205              
 206              // disable actual context-menu
 207              e.preventDefault();
 208              e.stopImmediatePropagation();
 209              
 210              // abort native-triggered events unless we're triggering on right click
 211              if (e.data.trigger != 'right' && e.originalEvent) {
 212                  return;
 213              }
 214              
 215              if (!$this.hasClass('context-menu-disabled')) {
 216                  // theoretically need to fire a show event at <menu>
 217                  // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus
 218                  // var evt = jQuery.Event("show", { data: data, pageX: e.pageX, pageY: e.pageY, relatedTarget: this });
 219                  // e.data.$menu.trigger(evt);
 220                  
 221                  $currentTrigger = $this;
 222                  if (e.data.build) {
 223                      var built = e.data.build($currentTrigger, e);
 224                      // abort if build() returned false
 225                      if (built === false) {
 226                          return;
 227                      }
 228                      
 229                      // dynamically build menu on invocation
 230                      e.data = $.extend(true, {}, defaults, e.data, built || {});
 231  
 232                      // abort if there are no items to display
 233                      if (!e.data.items || $.isEmptyObject(e.data.items)) {
 234                          // Note: jQuery captures and ignores errors from event handlers
 235                          if (window.console) {
 236                              (console.error || console.log)("No items specified to show in contextMenu");
 237                          }
 238                          
 239                          throw new Error('No Items sepcified');
 240                      }
 241                      
 242                      // backreference for custom command type creation
 243                      e.data.$trigger = $currentTrigger;
 244                      
 245                      op.create(e.data);
 246                  }
 247                  // show menu
 248                  op.show.call($this, e.data, e.pageX, e.pageY);
 249              }
 250          },
 251          // contextMenu left-click trigger
 252          click: function(e) {
 253              e.preventDefault();
 254              e.stopImmediatePropagation();
 255              $(this).trigger($.Event("contextmenu", { data: e.data, pageX: e.pageX, pageY: e.pageY }));
 256          },
 257          // contextMenu right-click trigger
 258          mousedown: function(e) {
 259              // register mouse down
 260              var $this = $(this);
 261              
 262              // hide any previous menus
 263              if ($currentTrigger && $currentTrigger.length && !$currentTrigger.is($this)) {
 264                  $currentTrigger.data('contextMenu').$menu.trigger('contextmenu:hide');
 265              }
 266              
 267              // activate on right click
 268              if (e.button == 2) {
 269                  $currentTrigger = $this.data('contextMenuActive', true);
 270              }
 271          },
 272          // contextMenu right-click trigger
 273          mouseup: function(e) {
 274              // show menu
 275              var $this = $(this);
 276              if ($this.data('contextMenuActive') && $currentTrigger && $currentTrigger.length && $currentTrigger.is($this) && !$this.hasClass('context-menu-disabled')) {
 277                  e.preventDefault();
 278                  e.stopImmediatePropagation();
 279                  $currentTrigger = $this;
 280                  $this.trigger($.Event("contextmenu", { data: e.data, pageX: e.pageX, pageY: e.pageY }));
 281              }
 282              
 283              $this.removeData('contextMenuActive');
 284          },
 285          // contextMenu hover trigger
 286          mouseenter: function(e) {
 287              var $this = $(this),
 288                  $related = $(e.relatedTarget),
 289                  $document = $(document);
 290              
 291              // abort if we're coming from a menu
 292              if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) {
 293                  return;
 294              }
 295              
 296              // abort if a menu is shown
 297              if ($currentTrigger && $currentTrigger.length) {
 298                  return;
 299              }
 300              
 301              hoveract.pageX = e.pageX;
 302              hoveract.pageY = e.pageY;
 303              hoveract.data = e.data;
 304              $document.on('mousemove.contextMenuShow', handle.mousemove);
 305              hoveract.timer = setTimeout(function() {
 306                  hoveract.timer = null;
 307                  $document.off('mousemove.contextMenuShow');
 308                  $currentTrigger = $this;
 309                  $this.trigger($.Event("contextmenu", { data: hoveract.data, pageX: hoveract.pageX, pageY: hoveract.pageY }));
 310              }, e.data.delay );
 311          },
 312          // contextMenu hover trigger
 313          mousemove: function(e) {
 314              hoveract.pageX = e.pageX;
 315              hoveract.pageY = e.pageY;
 316          },
 317          // contextMenu hover trigger
 318          mouseleave: function(e) {
 319              // abort if we're leaving for a menu
 320              var $related = $(e.relatedTarget);
 321              if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) {
 322                  return;
 323              }
 324              
 325              try {
 326                  clearTimeout(hoveract.timer);
 327              } catch(e) {}
 328              
 329              hoveract.timer = null;
 330          },
 331          
 332          // click on layer to hide contextMenu
 333          layerClick: function(e) {
 334              var $this = $(this),
 335                  root = $this.data('contextMenuRoot'),
 336                  mouseup = false,
 337                  button = e.button,
 338                  x = e.pageX,
 339                  y = e.pageY,
 340                  target, 
 341                  offset,
 342                  selectors;
 343                  
 344              e.preventDefault();
 345              e.stopImmediatePropagation();
 346              
 347              // This hack looks about as ugly as it is
 348              // Firefox 12 (at least) fires the contextmenu event directly "after" mousedown
 349              // for some reason `root.$layer.hide(); document.elementFromPoint()` causes this
 350              // contextmenu event to be triggered on the uncovered element instead of on the
 351              // layer (where every other sane browser, including Firefox nightly at the time)
 352              // triggers the event. This workaround might be obsolete by September 2012.
 353              $this.on('mouseup', function() {
 354                  mouseup = true;
 355              });
 356              setTimeout(function() {
 357                  var $window, hideshow;
 358                  // test if we need to reposition the menu
 359                  if ((root.trigger == 'left' && button == 0) || (root.trigger == 'right' && button == 2)) {
 360                      if (document.elementFromPoint) {
 361                          root.$layer.hide();
 362                          target = document.elementFromPoint(x - $win.scrollLeft(), y - $win.scrollTop());
 363                          root.$layer.show();
 364  
 365                          selectors = [];
 366                          for (var s in namespaces) {
 367                              selectors.push(s);
 368                          }
 369  
 370                          target = $(target).closest(selectors.join(', '));
 371  
 372                          if (target.length) {
 373                              if (target.is(root.$trigger[0])) {
 374                                  root.position.call(root.$trigger, root, x, y);
 375                                  return;
 376                              }
 377                          }
 378                      } else {
 379                          offset = root.$trigger.offset();
 380                          $window = $(window);
 381                          // while this looks kinda awful, it's the best way to avoid
 382                          // unnecessarily calculating any positions
 383                          offset.top += $window.scrollTop();
 384                          if (offset.top <= e.pageY) {
 385                              offset.left += $window.scrollLeft();
 386                              if (offset.left <= e.pageX) {
 387                                  offset.bottom = offset.top + root.$trigger.outerHeight();
 388                                  if (offset.bottom >= e.pageY) {
 389                                      offset.right = offset.left + root.$trigger.outerWidth();
 390                                      if (offset.right >= e.pageX) {
 391                                          // reposition
 392                                          root.position.call(root.$trigger, root, x, y);
 393                                          return;
 394                                      }
 395                                  }
 396                              }
 397                          }
 398                      }
 399                  }
 400  
 401                  hideshow = function(e) {
 402                      if (e) {
 403                          e.preventDefault();
 404                          e.stopImmediatePropagation();
 405                      }
 406  
 407                      root.$menu.trigger('contextmenu:hide');
 408                      if (target && target.length) {
 409                          setTimeout(function() {
 410                              target.contextMenu({x: x, y: y});
 411                          }, 50);
 412                      }
 413                  };
 414              
 415                  if (mouseup) {
 416                      // mouseup has already happened
 417                      hideshow();
 418                  } else {
 419                      // remove only after mouseup has completed
 420                      $this.on('mouseup', hideshow);
 421                  }
 422              }, 50);
 423          },
 424          // key handled :hover
 425          keyStop: function(e, opt) {
 426              if (!opt.isInput) {
 427                  e.preventDefault();
 428              }
 429              
 430              e.stopPropagation();
 431          },
 432          key: function(e) {
 433              var opt = $currentTrigger.data('contextMenu') || {},
 434                  $children = opt.$menu.children(),
 435                  $round;
 436  
 437              switch (e.keyCode) {
 438                  case 9:
 439                  case 38: // up
 440                      handle.keyStop(e, opt);
 441                      // if keyCode is [38 (up)] or [9 (tab) with shift]
 442                      if (opt.isInput) {
 443                          if (e.keyCode == 9 && e.shiftKey) {
 444                              e.preventDefault();
 445                              opt.$selected && opt.$selected.find('input, textarea, select').blur();
 446                              opt.$menu.trigger('prevcommand');
 447                              return;
 448                          } else if (e.keyCode == 38 && opt.$selected.find('input, textarea, select').prop('type') == 'checkbox') {
 449                              // checkboxes don't capture this key
 450                              e.preventDefault();
 451                              return;
 452                          }
 453                      } else if (e.keyCode != 9 || e.shiftKey) {
 454                          opt.$menu.trigger('prevcommand');
 455                          return;
 456                      }
 457                      // omitting break;
 458                      
 459                  // case 9: // tab - reached through omitted break;
 460                  case 40: // down
 461                      handle.keyStop(e, opt);
 462                      if (opt.isInput) {
 463                          if (e.keyCode == 9) {
 464                              e.preventDefault();
 465                              opt.$selected && opt.$selected.find('input, textarea, select').blur();
 466                              opt.$menu.trigger('nextcommand');
 467                              return;
 468                          } else if (e.keyCode == 40 && opt.$selected.find('input, textarea, select').prop('type') == 'checkbox') {
 469                              // checkboxes don't capture this key
 470                              e.preventDefault();
 471                              return;
 472                          }
 473                      } else {
 474                          opt.$menu.trigger('nextcommand');
 475                          return;
 476                      }
 477                      break;
 478                  
 479                  case 37: // left
 480                      handle.keyStop(e, opt);
 481                      if (opt.isInput || !opt.$selected || !opt.$selected.length) {
 482                          break;
 483                      }
 484                  
 485                      if (!opt.$selected.parent().hasClass('context-menu-root')) {
 486                          var $parent = opt.$selected.parent().parent();
 487                          opt.$selected.trigger('contextmenu:blur');
 488                          opt.$selected = $parent;
 489                          return;
 490                      }
 491                      break;
 492                      
 493                  case 39: // right
 494                      handle.keyStop(e, opt);
 495                      if (opt.isInput || !opt.$selected || !opt.$selected.length) {
 496                          break;
 497                      }
 498                      
 499                      var itemdata = opt.$selected.data('contextMenu') || {};
 500                      if (itemdata.$menu && opt.$selected.hasClass('context-menu-submenu')) {
 501                          opt.$selected = null;
 502                          itemdata.$selected = null;
 503                          itemdata.$menu.trigger('nextcommand');
 504                          return;
 505                      }
 506                      break;
 507                  
 508                  case 35: // end
 509                  case 36: // home
 510                      if (opt.$selected && opt.$selected.find('input, textarea, select').length) {
 511                          return;
 512                      } else {
 513                          (opt.$selected && opt.$selected.parent() || opt.$menu)
 514                              .children(':not(.disabled, .not-selectable)')[e.keyCode == 36 ? 'first' : 'last']()
 515                              .trigger('contextmenu:focus');
 516                          e.preventDefault();
 517                          return;
 518                      }
 519                      break;
 520                      
 521                  case 13: // enter
 522                      handle.keyStop(e, opt);
 523                      if (opt.isInput) {
 524                          if (opt.$selected && !opt.$selected.is('textarea, select')) {
 525                              e.preventDefault();
 526                              return;
 527                          }
 528                          break;
 529                      }
 530                      opt.$selected && opt.$selected.trigger('mouseup');
 531                      return;
 532                      
 533                  case 32: // space
 534                  case 33: // page up
 535                  case 34: // page down
 536                      // prevent browser from scrolling down while menu is visible
 537                      handle.keyStop(e, opt);
 538                      return;
 539                      
 540                  case 27: // esc
 541                      handle.keyStop(e, opt);
 542                      opt.$menu.trigger('contextmenu:hide');
 543                      return;
 544                      
 545                  default: // 0-9, a-z
 546                      var k = (String.fromCharCode(e.keyCode)).toUpperCase();
 547                      if (opt.accesskeys[k]) {
 548                          // according to the specs accesskeys must be invoked immediately
 549                          opt.accesskeys[k].$node.trigger(opt.accesskeys[k].$menu
 550                              ? 'contextmenu:focus'
 551                              : 'mouseup'
 552                          );
 553                          return;
 554                      }
 555                      break;
 556              }
 557              // pass event to selected item, 
 558              // stop propagation to avoid endless recursion
 559              e.stopPropagation();
 560              opt.$selected && opt.$selected.trigger(e);
 561          },
 562  
 563          // select previous possible command in menu
 564          prevItem: function(e) {
 565              e.stopPropagation();
 566              var opt = $(this).data('contextMenu') || {};
 567  
 568              // obtain currently selected menu
 569              if (opt.$selected) {
 570                  var $s = opt.$selected;
 571                  opt = opt.$selected.parent().data('contextMenu') || {};
 572                  opt.$selected = $s;
 573              }
 574              
 575              var $children = opt.$menu.children(),
 576                  $prev = !opt.$selected || !opt.$selected.prev().length ? $children.last() : opt.$selected.prev(),
 577                  $round = $prev;
 578              
 579              // skip disabled
 580              while ($prev.hasClass('disabled') || $prev.hasClass('not-selectable')) {
 581                  if ($prev.prev().length) {
 582                      $prev = $prev.prev();
 583                  } else {
 584                      $prev = $children.last();
 585                  }
 586                  if ($prev.is($round)) {
 587                      // break endless loop
 588                      return;
 589                  }
 590              }
 591              
 592              // leave current
 593              if (opt.$selected) {
 594                  handle.itemMouseleave.call(opt.$selected.get(0), e);
 595              }
 596              
 597              // activate next
 598              handle.itemMouseenter.call($prev.get(0), e);
 599              
 600              // focus input
 601              var $input = $prev.find('input, textarea, select');
 602              if ($input.length) {
 603                  $input.focus();
 604              }
 605          },
 606          // select next possible command in menu
 607          nextItem: function(e) {
 608              e.stopPropagation();
 609              var opt = $(this).data('contextMenu') || {};
 610  
 611              // obtain currently selected menu
 612              if (opt.$selected) {
 613                  var $s = opt.$selected;
 614                  opt = opt.$selected.parent().data('contextMenu') || {};
 615                  opt.$selected = $s;
 616              }
 617  
 618              var $children = opt.$menu.children(),
 619                  $next = !opt.$selected || !opt.$selected.next().length ? $children.first() : opt.$selected.next(),
 620                  $round = $next;
 621  
 622              // skip disabled
 623              while ($next.hasClass('disabled') || $next.hasClass('not-selectable')) {
 624                  if ($next.next().length) {
 625                      $next = $next.next();
 626                  } else {
 627                      $next = $children.first();
 628                  }
 629                  if ($next.is($round)) {
 630                      // break endless loop
 631                      return;
 632                  }
 633              }
 634              
 635              // leave current
 636              if (opt.$selected) {
 637                  handle.itemMouseleave.call(opt.$selected.get(0), e);
 638              }
 639              
 640              // activate next
 641              handle.itemMouseenter.call($next.get(0), e);
 642              
 643              // focus input
 644              var $input = $next.find('input, textarea, select');
 645              if ($input.length) {
 646                  $input.focus();
 647              }
 648          },
 649          
 650          // flag that we're inside an input so the key handler can act accordingly
 651          focusInput: function(e) {
 652              var $this = $(this).closest('.context-menu-item'),
 653                  data = $this.data(),
 654                  opt = data.contextMenu,
 655                  root = data.contextMenuRoot;
 656  
 657              root.$selected = opt.$selected = $this;
 658              root.isInput = opt.isInput = true;
 659          },
 660          // flag that we're inside an input so the key handler can act accordingly
 661          blurInput: function(e) {
 662              var $this = $(this).closest('.context-menu-item'),
 663                  data = $this.data(),
 664                  opt = data.contextMenu,
 665                  root = data.contextMenuRoot;
 666  
 667              root.isInput = opt.isInput = false;
 668          },
 669          
 670          // :hover on menu
 671          menuMouseenter: function(e) {
 672              var root = $(this).data().contextMenuRoot;
 673              root.hovering = true;
 674          },
 675          // :hover on menu
 676          menuMouseleave: function(e) {
 677              var root = $(this).data().contextMenuRoot;
 678              if (root.$layer && root.$layer.is(e.relatedTarget)) {
 679                  root.hovering = false;
 680              }
 681          },
 682          
 683          // :hover done manually so key handling is possible
 684          itemMouseenter: function(e) {
 685              var $this = $(this),
 686                  data = $this.data(),
 687                  opt = data.contextMenu,
 688                  root = data.contextMenuRoot;
 689              
 690              root.hovering = true;
 691  
 692              // abort if we're re-entering
 693              if (e && root.$layer && root.$layer.is(e.relatedTarget)) {
 694                  e.preventDefault();
 695                  e.stopImmediatePropagation();
 696              }
 697  
 698              // make sure only one item is selected
 699              (opt.$menu ? opt : root).$menu
 700                  .children('.hover').trigger('contextmenu:blur');
 701  
 702              if ($this.hasClass('disabled') || $this.hasClass('not-selectable')) {
 703                  opt.$selected = null;
 704                  return;
 705              }
 706              
 707              $this.trigger('contextmenu:focus');
 708          },
 709          // :hover done manually so key handling is possible
 710          itemMouseleave: function(e) {
 711              var $this = $(this),
 712                  data = $this.data(),
 713                  opt = data.contextMenu,
 714                  root = data.contextMenuRoot;
 715  
 716              if (root !== opt && root.$layer && root.$layer.is(e.relatedTarget)) {
 717                  root.$selected && root.$selected.trigger('contextmenu:blur');
 718                  e.preventDefault();
 719                  e.stopImmediatePropagation();
 720                  root.$selected = opt.$selected = opt.$node;
 721                  return;
 722              }
 723              
 724              $this.trigger('contextmenu:blur');
 725          },
 726          // contextMenu item click
 727          itemClick: function(e) {
 728              var $this = $(this),
 729                  data = $this.data(),
 730                  opt = data.contextMenu,
 731                  root = data.contextMenuRoot,
 732                  key = data.contextMenuKey,
 733                  callback;
 734  
 735              // abort if the key is unknown or disabled or is a menu
 736              if (!opt.items[key] || $this.hasClass('disabled') || $this.hasClass('context-menu-submenu')) {
 737                  return;
 738              }
 739  
 740              e.preventDefault();
 741              e.stopImmediatePropagation();
 742  
 743              if ($.isFunction(root.callbacks[key])) {
 744                  // item-specific callback
 745                  callback = root.callbacks[key];
 746              } else if ($.isFunction(root.callback)) {
 747                  // default callback
 748                  callback = root.callback;                
 749              } else {
 750                  // no callback, no action
 751                  return;
 752              }
 753  
 754              // hide menu if callback doesn't stop that
 755              if (callback.call(root.$trigger, key, root) !== false) {
 756                  root.$menu.trigger('contextmenu:hide');
 757              } else if (root.$menu.parent().length) {
 758                  op.update.call(root.$trigger, root);
 759              }
 760          },
 761          // ignore click events on input elements
 762          inputClick: function(e) {
 763              e.stopImmediatePropagation();
 764          },
 765          
 766          // hide <menu>
 767          hideMenu: function(e, data) {
 768              var root = $(this).data('contextMenuRoot');
 769              op.hide.call(root.$trigger, root, data && data.force);
 770          },
 771          // focus <command>
 772          focusItem: function(e) {
 773              e.stopPropagation();
 774              var $this = $(this),
 775                  data = $this.data(),
 776                  opt = data.contextMenu,
 777                  root = data.contextMenuRoot;
 778  
 779              $this.addClass('hover')
 780                  .siblings('.hover').trigger('contextmenu:blur');
 781              
 782              // remember selected
 783              opt.$selected = root.$selected = $this;
 784              
 785              // position sub-menu - do after show so dumb $.ui.position can keep up
 786              if (opt.$node) {
 787                  root.positionSubmenu.call(opt.$node, opt.$menu);
 788              }
 789          },
 790          // blur <command>
 791          blurItem: function(e) {
 792              e.stopPropagation();
 793              var $this = $(this),
 794                  data = $this.data(),
 795                  opt = data.contextMenu,
 796                  root = data.contextMenuRoot;
 797              
 798              $this.removeClass('hover');
 799              opt.$selected = null;
 800          }
 801      },
 802      // operations
 803      op = {
 804          show: function(opt, x, y) {
 805              var $this = $(this),
 806                  offset,
 807                  css = {};
 808  
 809              // hide any open menus
 810              $('#context-menu-layer').trigger('mousedown');
 811  
 812              // backreference for callbacks
 813              opt.$trigger = $this;
 814  
 815              // show event
 816              if (opt.events.show.call($this, opt) === false) {
 817                  $currentTrigger = null;
 818                  return;
 819              }
 820  
 821              // create or update context menu
 822              op.update.call($this, opt);
 823              
 824              // position menu
 825              opt.position.call($this, opt, x, y);
 826  
 827              // make sure we're in front
 828              if (opt.zIndex) {
 829                  css.zIndex = zindex($this) + opt.zIndex;
 830              }
 831              
 832              // add layer
 833              op.layer.call(opt.$menu, opt, css.zIndex);
 834              
 835              // adjust sub-menu zIndexes
 836              opt.$menu.find('ul').css('zIndex', css.zIndex + 1);
 837              
 838              // position and show context menu
 839              opt.$menu.css( css )[opt.animation.show](opt.animation.duration);
 840              // make options available
 841              $this.data('contextMenu', opt);
 842              // register key handler
 843              $(document).off('keydown.contextMenu').on('keydown.contextMenu', handle.key);
 844              // register autoHide handler
 845              if (opt.autoHide) {
 846                  // trigger element coordinates
 847                  var pos = $this.position();
 848                  pos.right = pos.left + $this.outerWidth();
 849                  pos.bottom = pos.top + this.outerHeight();
 850                  // mouse position handler
 851                  $(document).on('mousemove.contextMenuAutoHide', function(e) {
 852                      if (opt.$layer && !opt.hovering && (!(e.pageX >= pos.left && e.pageX <= pos.right) || !(e.pageY >= pos.top && e.pageY <= pos.bottom))) {
 853                          // if mouse in menu...
 854                          opt.$menu.trigger('contextmenu:hide');
 855                      }
 856                  });
 857              }
 858          },
 859          hide: function(opt, force) {
 860              var $this = $(this);
 861              if (!opt) {
 862                  opt = $this.data('contextMenu') || {};
 863              }
 864              
 865              // hide event
 866              if (!force && opt.events && opt.events.hide.call($this, opt) === false) {
 867                  return;
 868              }
 869              
 870              if (opt.$layer) {
 871                  // keep layer for a bit so the contextmenu event can be aborted properly by opera
 872                  setTimeout((function($layer){ return function(){
 873                          $layer.remove();
 874                      };
 875                  })(opt.$layer), 10);
 876                  
 877                  try {
 878                      delete opt.$layer;
 879                  } catch(e) {
 880                      opt.$layer = null;
 881                  }
 882              }
 883              
 884              // remove handle
 885              $currentTrigger = null;
 886              // remove selected
 887              opt.$menu.find('.hover').trigger('contextmenu:blur');
 888              opt.$selected = null;
 889              // unregister key and mouse handlers
 890              //$(document).off('.contextMenuAutoHide keydown.contextMenu'); // http://bugs.jquery.com/ticket/10705
 891              $(document).off('.contextMenuAutoHide').off('keydown.contextMenu');
 892              // hide menu
 893              opt.$menu && opt.$menu[opt.animation.hide](opt.animation.duration, function (){
 894                  // tear down dynamically built menu after animation is completed.
 895                  if (opt.build) {
 896                      opt.$menu.remove();
 897                      $.each(opt, function(key, value) {
 898                          switch (key) {
 899                              case 'ns':
 900                              case 'selector':
 901                              case 'build':
 902                              case 'trigger':
 903                                  return true;
 904  
 905                              default:
 906                                  opt[key] = undefined;
 907                                  try {
 908                                      delete opt[key];
 909                                  } catch (e) {}
 910                                  return true;
 911                          }
 912                      });
 913                  }
 914              });
 915          },
 916          create: function(opt, root) {
 917              if (root === undefined) {
 918                  root = opt;
 919              }
 920              // create contextMenu
 921              opt.$menu = $('<ul class="context-menu-list ' + (opt.className || "") + '"></ul>').data({
 922                  'contextMenu': opt,
 923                  'contextMenuRoot': root
 924              });
 925              
 926              $.each(['callbacks', 'commands', 'inputs'], function(i,k){
 927                  opt[k] = {};
 928                  if (!root[k]) {
 929                      root[k] = {};
 930                  }
 931              });
 932              
 933              root.accesskeys || (root.accesskeys = {});
 934              
 935              // create contextMenu items
 936              $.each(opt.items, function(key, item){
 937                  var $t = $('<li class="context-menu-item ' + (item.className || "") +'"></li>'),
 938                      $label = null,
 939                      $input = null;
 940                  
 941                  item.$node = $t.data({
 942                      'contextMenu': opt,
 943                      'contextMenuRoot': root,
 944                      'contextMenuKey': key
 945                  });
 946                  
 947                  // register accesskey
 948                  // NOTE: the accesskey attribute should be applicable to any element, but Safari5 and Chrome13 still can't do that
 949                  if (item.accesskey) {
 950                      var aks = splitAccesskey(item.accesskey);
 951                      for (var i=0, ak; ak = aks[i]; i++) {
 952                          if (!root.accesskeys[ak]) {
 953                              root.accesskeys[ak] = item;
 954                              item._name = item.name.replace(new RegExp('(' + ak + ')', 'i'), '<span class="context-menu-accesskey">$1</span>');
 955                              break;
 956                          }
 957                      }
 958                  }
 959                  
 960                  if (typeof item == "string") {
 961                      $t.addClass('context-menu-separator not-selectable');
 962                  } else if (item.type && types[item.type]) {
 963                      // run custom type handler
 964                      types[item.type].call($t, item, opt, root);
 965                      // register commands
 966                      $.each([opt, root], function(i,k){
 967                          k.commands[key] = item;
 968                          if ($.isFunction(item.callback)) {
 969                              k.callbacks[key] = item.callback;
 970                          }
 971                      });
 972                  } else {
 973                      // add label for input
 974                      if (item.type == 'html') {
 975                          $t.addClass('context-menu-html not-selectable');
 976                      } else if (item.type) {
 977                          $label = $('<label></label>').appendTo($t);
 978                          $('<span></span>').html(item._name || item.name).appendTo($label);
 979                          $t.addClass('context-menu-input');
 980                          opt.hasTypes = true;
 981                          $.each([opt, root], function(i,k){
 982                              k.commands[key] = item;
 983                              k.inputs[key] = item;
 984                          });
 985                      } else if (item.items) {
 986                          item.type = 'sub';
 987                      }
 988                  
 989                      switch (item.type) {
 990                          case 'text':
 991                              $input = $('<input type="text" value="1" name="context-menu-input-'+ key +'" value="">')
 992                                  .val(item.value || "").appendTo($label);
 993                              break;
 994                      
 995                          case 'textarea':
 996                              $input = $('<textarea name="context-menu-input-'+ key +'"></textarea>')
 997                                  .val(item.value || "").appendTo($label);
 998  
 999                              if (item.height) {
1000                                  $input.height(item.height);
1001                              }
1002                              break;
1003  
1004                          case 'checkbox':
1005                              $input = $('<input type="checkbox" value="1" name="context-menu-input-'+ key +'" value="">')
1006                                  .val(item.value || "").prop("checked", !!item.selected).prependTo($label);
1007                              break;
1008  
1009                          case 'radio':
1010                              $input = $('<input type="radio" value="1" name="context-menu-input-'+ item.radio +'" value="">')
1011                                  .val(item.value || "").prop("checked", !!item.selected).prependTo($label);
1012                              break;
1013                      
1014                          case 'select':
1015                              $input = $('<select name="context-menu-input-'+ key +'">').appendTo($label);
1016                              if (item.options) {
1017                                  $.each(item.options, function(value, text) {
1018                                      $('<option></option>').val(value).text(text).appendTo($input);
1019                                  });
1020                                  $input.val(item.selected);
1021                              }
1022                              break;
1023                          
1024                          case 'sub':
1025                              $('<span></span>').html(item._name || item.name).appendTo($t);
1026                              item.appendTo = item.$node;
1027                              op.create(item, root);
1028                              $t.data('contextMenu', item).addClass('context-menu-submenu');
1029                              item.callback = null;
1030                              break;
1031                          
1032                          case 'html':
1033                              $(item.html).appendTo($t);
1034                              break;
1035                          
1036                          default:
1037                              $.each([opt, root], function(i,k){
1038                                  k.commands[key] = item;
1039                                  if ($.isFunction(item.callback)) {
1040                                      k.callbacks[key] = item.callback;
1041                                  }
1042                              });
1043                              
1044                              $('<span></span>').html(item._name || item.name || "").appendTo($t);
1045                              break;
1046                      }
1047                      
1048                      // disable key listener in <input>
1049                      if (item.type && item.type != 'sub' && item.type != 'html') {
1050                          $input
1051                              .on('focus', handle.focusInput)
1052                              .on('blur', handle.blurInput);
1053                          
1054                          if (item.events) {
1055                              $input.on(item.events, opt);
1056                          }
1057                      }
1058                  
1059                      // add icons
1060                      if (item.icon) {
1061                          $t.addClass("icon icon-" + item.icon);
1062                      }
1063                  }
1064                  
1065                  // cache contained elements
1066                  item.$input = $input;
1067                  item.$label = $label;
1068  
1069                  // attach item to menu
1070                  $t.appendTo(opt.$menu);
1071                  
1072                  // Disable text selection
1073                  if (!opt.hasTypes && $.support.eventSelectstart) {
1074                      // browsers support user-select: none, 
1075                      // IE has a special event for text-selection
1076                      // browsers supporting neither will not be preventing text-selection
1077                      $t.on('selectstart.disableTextSelect', handle.abortevent);
1078                  }
1079              });
1080              // attach contextMenu to <body> (to bypass any possible overflow:hidden issues on parents of the trigger element)
1081              if (!opt.$node) {
1082                  opt.$menu.css('display', 'none').addClass('context-menu-root');
1083              }
1084              opt.$menu.appendTo(opt.appendTo || document.body);
1085          },
1086          update: function(opt, root) {
1087              var $this = this;
1088              if (root === undefined) {
1089                  root = opt;
1090                  // determine widths of submenus, as CSS won't grow them automatically
1091                  // position:absolute > position:absolute; min-width:100; max-width:200; results in width: 100;
1092                  // kinda sucks hard...
1093                  opt.$menu.find('ul').andSelf().css({position: 'static', display: 'block'}).each(function(){
1094                      var $this = $(this);
1095                      $this.width($this.css('position', 'absolute').width())
1096                          .css('position', 'static');
1097                  }).css({position: '', display: ''});
1098              }
1099              // re-check disabled for each item
1100              opt.$menu.children().each(function(){
1101                  var $item = $(this),
1102                      key = $item.data('contextMenuKey'),
1103                      item = opt.items[key],
1104                      disabled = ($.isFunction(item.disabled) && item.disabled.call($this, key, root)) || item.disabled === true;
1105  
1106                  // dis- / enable item
1107                  $item[disabled ? 'addClass' : 'removeClass']('disabled');
1108                  
1109                  if (item.type) {
1110                      // dis- / enable input elements
1111                      $item.find('input, select, textarea').prop('disabled', disabled);
1112                      
1113                      // update input states
1114                      switch (item.type) {
1115                          case 'text':
1116                          case 'textarea':
1117                              item.$input.val(item.value || "");
1118                              break;
1119                              
1120                          case 'checkbox':
1121                          case 'radio':
1122                              item.$input.val(item.value || "").prop('checked', !!item.selected);
1123                              break;
1124                              
1125                          case 'select':
1126                              item.$input.val(item.selected || "");
1127                              break;
1128                      }
1129                  }
1130                  
1131                  if (item.$menu) {
1132                      // update sub-menu
1133                      op.update.call($this, item, root);
1134                  }
1135              });
1136          },
1137          layer: function(opt, zIndex) {
1138              // add transparent layer for click area
1139              // filter and background for Internet Explorer, Issue #23
1140              var $layer = opt.$layer = $('<div id="context-menu-layer" style="position:fixed; z-index:' + zIndex + '; top:0; left:0; opacity: 0; filter: alpha(opacity=0); background-color: #000;"></div>')
1141                  .css({height: $win.height(), width: $win.width(), display: 'block'})
1142                  .data('contextMenuRoot', opt)
1143                  .insertBefore(this)
1144                  .on('contextmenu', handle.abortevent)
1145                  .on('mousedown', handle.layerClick);
1146              
1147              // IE6 doesn't know position:fixed;
1148              if (!$.support.fixedPosition) {
1149                  $layer.css({
1150                      'position' : 'absolute',
1151                      'height' : $(document).height()
1152                  });
1153              }
1154              
1155              return $layer;
1156          }
1157      };
1158  
1159  // split accesskey according to http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#assigned-access-key
1160  function splitAccesskey(val) {
1161      var t = val.split(/\s+/),
1162          keys = [];
1163          
1164      for (var i=0, k; k = t[i]; i++) {
1165          k = k[0].toUpperCase(); // first character only
1166          // theoretically non-accessible characters should be ignored, but different systems, different keyboard layouts, ... screw it.
1167          // a map to look up already used access keys would be nice
1168          keys.push(k);
1169      }
1170      
1171      return keys;
1172  }
1173  
1174  // handle contextMenu triggers
1175  $.fn.contextMenu = function(operation) {
1176      if (operation === undefined) {
1177          this.first().trigger('contextmenu');
1178      } else if (operation.x && operation.y) {
1179          this.first().trigger($.Event("contextmenu", {pageX: operation.x, pageY: operation.y}));
1180      } else if (operation === "hide") {
1181          var $menu = this.data('contextMenu').$menu;
1182          $menu && $menu.trigger('contextmenu:hide');
1183      } else if (operation) {
1184          this.removeClass('context-menu-disabled');
1185      } else if (!operation) {
1186          this.addClass('context-menu-disabled');
1187      }
1188      
1189      return this;
1190  };
1191  
1192  // manage contextMenu instances
1193  $.contextMenu = function(operation, options) {
1194      if (typeof operation != 'string') {
1195          options = operation;
1196          operation = 'create';
1197      }
1198      
1199      if (typeof options == 'string') {
1200          options = {selector: options};
1201      } else if (options === undefined) {
1202          options = {};
1203      }
1204      
1205      // merge with default options
1206      var o = $.extend(true, {}, defaults, options || {}),
1207          $document = $(document);
1208      
1209      switch (operation) {
1210          case 'create':
1211              // no selector no joy
1212              if (!o.selector) {
1213                  throw new Error('No selector specified');
1214              }
1215              // make sure internal classes are not bound to
1216              if (o.selector.match(/.context-menu-(list|item|input)($|\s)/)) {
1217                  throw new Error('Cannot bind to selector "' + o.selector + '" as it contains a reserved className');
1218              }
1219              if (!o.build && (!o.items || $.isEmptyObject(o.items))) {
1220                  throw new Error('No Items sepcified');
1221              }
1222              counter ++;
1223              o.ns = '.contextMenu' + counter;
1224              namespaces[o.selector] = o.ns;
1225              menus[o.ns] = o;
1226              
1227              // default to right click
1228              if (!o.trigger) {
1229                  o.trigger = 'right';
1230              }
1231              
1232              if (!initialized) {
1233                  // make sure item click is registered first
1234                  $document
1235                      .on({
1236                          'contextmenu:hide.contextMenu': handle.hideMenu,
1237                          'prevcommand.contextMenu': handle.prevItem,
1238                          'nextcommand.contextMenu': handle.nextItem,
1239                          'contextmenu.contextMenu': handle.abortevent,
1240                          'mouseenter.contextMenu': handle.menuMouseenter,
1241                          'mouseleave.contextMenu': handle.menuMouseleave
1242                      }, '.context-menu-list')
1243                      .on('mouseup.contextMenu', '.context-menu-input', handle.inputClick)
1244                      .on({
1245                          'mouseup.contextMenu': handle.itemClick,
1246                          'contextmenu:focus.contextMenu': handle.focusItem,
1247                          'contextmenu:blur.contextMenu': handle.blurItem,
1248                          'contextmenu.contextMenu': handle.abortevent,
1249                          'mouseenter.contextMenu': handle.itemMouseenter,
1250                          'mouseleave.contextMenu': handle.itemMouseleave
1251                      }, '.context-menu-item');
1252  
1253                  initialized = true;
1254              }
1255              
1256              // engage native contextmenu event
1257              $document
1258                  .on('contextmenu' + o.ns, o.selector, o, handle.contextmenu);
1259              
1260              switch (o.trigger) {
1261                  case 'hover':
1262                          $document
1263                              .on('mouseenter' + o.ns, o.selector, o, handle.mouseenter)
1264                              .on('mouseleave' + o.ns, o.selector, o, handle.mouseleave);                    
1265                      break;
1266                      
1267                  case 'left':
1268                          $document.on('click' + o.ns, o.selector, o, handle.click);
1269                      break;
1270                  /*
1271                  default:
1272                      // http://www.quirksmode.org/dom/events/contextmenu.html
1273                      $document
1274                          .on('mousedown' + o.ns, o.selector, o, handle.mousedown)
1275                          .on('mouseup' + o.ns, o.selector, o, handle.mouseup);
1276                      break;
1277                  */
1278              }
1279              
1280              // create menu
1281              if (!o.build) {
1282                  op.create(o);
1283              }
1284              break;
1285          
1286          case 'destroy':
1287              if (!o.selector) {
1288                  $document.off('.contextMenu .contextMenuAutoHide');
1289                  $.each(namespaces, function(key, value) {
1290                      $document.off(value);
1291                  });
1292                  
1293                  namespaces = {};
1294                  menus = {};
1295                  counter = 0;
1296                  initialized = false;
1297                  
1298                  $('#context-menu-layer, .context-menu-list').remove();
1299              } else if (namespaces[o.selector]) {
1300                  var $visibleMenu = $('.context-menu-list').filter(':visible');
1301                  if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is(o.selector)) {
1302                      $visibleMenu.trigger('contextmenu:hide', {force: true});
1303                  }
1304                  
1305                  try {
1306                      if (menus[namespaces[o.selector]].$menu) {
1307                          menus[namespaces[o.selector]].$menu.remove();
1308                      }
1309                      
1310                      delete menus[namespaces[o.selector]];
1311                  } catch(e) {
1312                      menus[namespaces[o.selector]] = null;
1313                  }
1314                  
1315                  $document.off(namespaces[o.selector]);
1316              }
1317              break;
1318          
1319          case 'html5':
1320              // if <command> or <menuitem> are not handled by the browser,
1321              // or options was a bool true,
1322              // initialize $.contextMenu for them
1323              if ((!$.support.htmlCommand && !$.support.htmlMenuitem) || (typeof options == "boolean" && options)) {
1324                  $('menu[type="context"]').each(function() {
1325                      if (this.id) {
1326                          $.contextMenu({
1327                              selector: '[contextmenu=' + this.id +']',
1328                              items: $.contextMenu.fromMenu(this)
1329                          });
1330                      }
1331                  }).css('display', 'none');
1332              }
1333              break;
1334          
1335          default:
1336              throw new Error('Unknown operation "' + operation + '"');
1337      }
1338      
1339      return this;
1340  };
1341  
1342  // import values into <input> commands
1343  $.contextMenu.setInputValues = function(opt, data) {
1344      if (data === undefined) {
1345          data = {};
1346      }
1347      
1348      $.each(opt.inputs, function(key, item) {
1349          switch (item.type) {
1350              case 'text':
1351              case 'textarea':
1352                  item.value = data[key] || "";
1353                  break;
1354  
1355              case 'checkbox':
1356                  item.selected = data[key] ? true : false;
1357                  break;
1358                  
1359              case 'radio':
1360                  item.selected = (data[item.radio] || "") == item.value ? true : false;
1361                  break;
1362              
1363              case 'select':
1364                  item.selected = data[key] || "";
1365                  break;
1366          }
1367      });
1368  };
1369  
1370  // export values from <input> commands
1371  $.contextMenu.getInputValues = function(opt, data) {
1372      if (data === undefined) {
1373          data = {};
1374      }
1375      
1376      $.each(opt.inputs, function(key, item) {
1377          switch (item.type) {
1378              case 'text':
1379              case 'textarea':
1380              case 'select':
1381                  data[key] = item.$input.val();
1382                  break;
1383  
1384              case 'checkbox':
1385                  data[key] = item.$input.prop('checked');
1386                  break;
1387                  
1388              case 'radio':
1389                  if (item.$input.prop('checked')) {
1390                      data[item.radio] = item.value;
1391                  }
1392                  break;
1393          }
1394      });
1395      
1396      return data;
1397  };
1398  
1399  // find <label for="xyz">
1400  function inputLabel(node) {
1401      return (node.id && $('label[for="'+ node.id +'"]').val()) || node.name;
1402  }
1403  
1404  // convert <menu> to items object
1405  function menuChildren(items, $children, counter) {
1406      if (!counter) {
1407          counter = 0;
1408      }
1409      
1410      $children.each(function() {
1411          var $node = $(this),
1412              node = this,
1413              nodeName = this.nodeName.toLowerCase(),
1414              label,
1415              item;
1416          
1417          // extract <label><input>
1418          if (nodeName == 'label' && $node.find('input, textarea, select').length) {
1419              label = $node.text();
1420              $node = $node.children().first();
1421              node = $node.get(0);
1422              nodeName = node.nodeName.toLowerCase();
1423          }
1424          
1425          /*
1426           * <menu> accepts flow-content as children. that means <embed>, <canvas> and such are valid menu items.
1427           * Not being the sadistic kind, $.contextMenu only accepts:
1428           * <command>, <menuitem>, <hr>, <span>, <p> <input [text, radio, checkbox]>, <textarea>, <select> and of course <menu>.
1429           * Everything else will be imported as an html node, which is not interfaced with contextMenu.
1430           */
1431          
1432          // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#concept-command
1433          switch (nodeName) {
1434              // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#the-menu-element
1435              case 'menu':
1436                  item = {name: $node.attr('label'), items: {}};
1437                  counter = menuChildren(item.items, $node.children(), counter);
1438                  break;
1439              
1440              // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command
1441              case 'a':
1442              // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command
1443              case 'button':
1444                  item = {
1445                      name: $node.text(),
1446                      disabled: !!$node.attr('disabled'),
1447                      callback: (function(){ return function(){ $node.click(); }; })()
1448                  };
1449                  break;
1450              
1451              // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-command-element-to-define-a-command
1452  
1453              case 'menuitem':
1454              case 'command':
1455                  switch ($node.attr('type')) {
1456                      case undefined:
1457                      case 'command':
1458                      case 'menuitem':
1459                          item = {
1460                              name: $node.attr('label'),
1461                              disabled: !!$node.attr('disabled'),
1462                              callback: (function(){ return function(){ $node.click(); }; })()
1463                          };
1464                          break;
1465                          
1466                      case 'checkbox':
1467                          item = {
1468                              type: 'checkbox',
1469                              disabled: !!$node.attr('disabled'),
1470                              name: $node.attr('label'),
1471                              selected: !!$node.attr('checked')
1472                          };
1473                          break;
1474                          
1475                      case 'radio':
1476                          item = {
1477                              type: 'radio',
1478                              disabled: !!$node.attr('disabled'),
1479                              name: $node.attr('label'),
1480                              radio: $node.attr('radiogroup'),
1481                              value: $node.attr('id'),
1482                              selected: !!$node.attr('checked')
1483                          };
1484                          break;
1485                          
1486                      default:
1487                          item = undefined;
1488                  }
1489                  break;
1490   
1491              case 'hr':
1492                  item = '-------';
1493                  break;
1494                  
1495              case 'input':
1496                  switch ($node.attr('type')) {
1497                      case 'text':
1498                          item = {
1499                              type: 'text',
1500                              name: label || inputLabel(node),
1501                              disabled: !!$node.attr('disabled'),
1502                              value: $node.val()
1503                          };
1504                          break;
1505                          
1506                      case 'checkbox':
1507                          item = {
1508                              type: 'checkbox',
1509                              name: label || inputLabel(node),
1510                              disabled: !!$node.attr('disabled'),
1511                              selected: !!$node.attr('checked')
1512                          };
1513                          break;
1514                          
1515                      case 'radio':
1516                          item = {
1517                              type: 'radio',
1518                              name: label || inputLabel(node),
1519                              disabled: !!$node.attr('disabled'),
1520                              radio: !!$node.attr('name'),
1521                              value: $node.val(),
1522                              selected: !!$node.attr('checked')
1523                          };
1524                          break;
1525                      
1526                      default:
1527                          item = undefined;
1528                          break;
1529                  }
1530                  break;
1531                  
1532              case 'select':
1533                  item = {
1534                      type: 'select',
1535                      name: label || inputLabel(node),
1536                      disabled: !!$node.attr('disabled'),
1537                      selected: $node.val(),
1538                      options: {}
1539                  };
1540                  $node.children().each(function(){
1541                      item.options[this.value] = $(this).text();
1542                  });
1543                  break;
1544                  
1545              case 'textarea':
1546                  item = {
1547                      type: 'textarea',
1548                      name: label || inputLabel(node),
1549                      disabled: !!$node.attr('disabled'),
1550                      value: $node.val()
1551                  };
1552                  break;
1553              
1554              case 'label':
1555                  break;
1556              
1557              default:
1558                  item = {type: 'html', html: $node.clone(true)};
1559                  break;
1560          }
1561          
1562          if (item) {
1563              counter++;
1564              items['key' + counter] = item;
1565          }
1566      });
1567      
1568      return counter;
1569  }
1570  
1571  // convert html5 menu
1572  $.contextMenu.fromMenu = function(element) {
1573      var $this = $(element),
1574          items = {};
1575          
1576      menuChildren(items, $this.children());
1577      
1578      return items;
1579  };
1580  
1581  // make defaults accessible
1582  $.contextMenu.defaults = defaults;
1583  $.contextMenu.types = types;
1584  
1585  })(jQuery);


Generated: Fri Nov 28 20:08:37 2014 Cross-referenced by PHPXref 0.7.1