/** Context menu class. You can create one or more context menus for a component by using the ludo.View.contextMenu config array, @namespace ludo.menu @class ludo.menu.Context @augments menu.Menu @param {Object} config @param {string} config.selector Show context menu only for DOM nodes matching a CSS selector. The context menu will also be shown if a match is found in one of the parent DOM elements. example: selector:'.my-class' @param {string} config.recordType Similar to selector, but focused on JSON data. It asks it's parent view's getRecordByDOM(e.target) (must be implemented), which will return something like { "name": "Oslo", type:"city" }. If config.recordType is "city", the context menu will be shown, otherwise it will not. This is typically useful in tree views and grids. @example new ludo.Window({ contextMenu:[{ selector : '.my-selector', children:[{label:'Menu Item 1'},{label:'Menu item 2'}], listeners:{ click : function(menuItem, menu){ // Do something } } }] }); */ ludo.menu.Context = new Class({ Extends:ludo.View, type:'menu.Context', layout:{ type:'Menu', orientation:'vertical', width:'wrap', height:'wrap', active:true, isContext:true }, renderTo:undefined, selector:undefined, component:undefined, // TODO change this code to record:{ keys that has to match }, example: record:{ type:'country' } record:undefined, recordType:undefined, contextEl:undefined, __construct:function (config) { this.renderTo = document.body; this.parent(config); this.setConfigParams(config, ['selector', 'recordType', 'record', 'applyTo','contextEl']); if (this.recordType)this.record = { type:this.recordType }; }, ludoDOM:function () { this.parent(); this.getEl().css('position', 'absolute'); }, ludoEvents:function () { this.parent(); $(document.documentElement).on('click', this.hideAfterDelay.bind(this)); if(this.contextEl){ $(this.contextEl).on('contextmenu', this.show.bind(this)); } }, hideAfterDelay:function () { if (!this.isHidden()) { this.hide.delay(50, this); } }, __rendered:function () { this.parent(); this.hide(); }, /** * when recordType property is defined, this will return the selected record of parent applyTo, * example: record in a tree * @function getSelectedRecord * @return object record * @memberof ludo.menu.Context.prototype */ getSelectedRecord:function () { return this.selectedRecord; }, show:function (e) { if (this.selector) { var domEl = this.getValidDomElement(e.target); if (!domEl) { return undefined; } this.fireEvent('selectorclick', domEl); } if (this.record) { var r = this.applyTo.getRecordByDOM(e.target); if (!r)return undefined; if (this.isContextMenuFor(r)) { this.selectedRecord = r; } } ludo.EffectObject.fireEvents(); this.getLayout().hideAllMenus(); this.parent(); if (!this.getParent()) { var el = this.getEl(); var pos = this.getXAndYPos(e); el.css({ left : pos.x + 'px', top : pos.y + 'px' }); } return false; }, isContextMenuFor:function (record) { if(jQuery.type(record) == "string" && this.record.type == record)return true; for (var key in this.record) { if (this.record.hasOwnProperty(key)) if (!record[key] || this.record[key] !== record[key])return false; } return true; }, getXAndYPos:function (e) { var ret = { x:e.pageX, y:e.pageY }; var b = $(document.body); var clientWidth = b.width(); var clientHeight = b.height(); var size = { x: this.getEl().width(), y: this.getEl().height() }; var x = ret.x + size.x; var y = ret.y + size.y; if (x > clientWidth) { ret.x -= (x - clientWidth); } if (y > clientHeight) { ret.y -= (y - clientHeight); } return ret; }, addCoreEvents:function () { }, getValidDomElement:function (el) { el = $(el); if (!this.selector) { return true; } var selector = this.selector.replace(/[\.#]/g, ''); if (el.hasClass(selector) || el.id == selector) { return el; } var parent = el.closest(this.selector); if (parent) { return parent; } return false; } });