1var RightClick = RightClick || {}; 2 3(function(window, $, exports, undefined) { 4 'use strict'; 5 6 exports.appName = 'files_rightclick'; 7 exports.selectors = { 8 containerId: 'rightClickMenus', 9 detectorId: 'rightClickDetector', 10 menuId: 'rightClickMenu', 11 menuClass: 'rightClickMenu', 12 subMenuClass: 'rightClickSubMenu', 13 openedClass: 'rightClickOpened', 14 arrowClass: 'rightClickArrow', 15 }; 16 exports.handableKeys = [ 17 'ctrl', 'shift', 'alt' 18 ]; 19 20 // Object where all options are listed for one (sub)menu 21 exports.Options = function (options) { 22 this.options = []; 23 24 this.getIndexFromOptionName = function (name) { 25 return this.getNbrOfOptions(); // TODO 26 }; 27 28 // Add one or more options 29 this.add = function (options, index) { 30 if (!(typeof index === 'number')) 31 index = this.getIndexFromOptionName(index); 32 33 if (index === undefined) 34 index = this.getNbrOfOptions(); 35 36 if (typeof options === 'string' || typeof options === 'number') 37 options = new exports.Option(options); 38 39 if (options instanceof exports.Option) 40 this.options.splice(index, 0, options); 41 else if (options !== undefined && Array.isArray(options)) { 42 for (var name in options) { 43 var option = options[name]; 44 45 if (typeof option !== 'function') { 46 if (typeof option === 'string' || typeof option === 'number') 47 option = new exports.Option(option); 48 } 49 50 if (option instanceof exports.Option) 51 this.options.splice(index, 0, option); 52 } 53 } 54 55 return this; 56 }; 57 58 this.prepend = function (options) { 59 return this.add(options, 0); 60 }; 61 62 this.append = function (options) { 63 return this.add(options, this.getNbrOfOptions()); 64 }; 65 66 // Generate all html options 67 this.generate = function () { 68 var ul = $('<ul>'); 69 70 for (var name in this.options) { 71 var li = this.options[name].generate(); 72 73 if (li) { 74 li.addClass('action-' + name); 75 ul.append(li); 76 } 77 } 78 79 return ul; 80 }; 81 82 this.getNbrOfOptions = function () { 83 return this.options.length; 84 }; 85 86 this.isDisabled = function () { 87 for (var name in this.options) { 88 if (!this.options[name].isDisabled()) 89 return false; 90 } 91 92 return true; 93 }; 94 95 this.isFirstDisabled = function () { 96 if (this.getNbrOfOptions() === 0) 97 return true; 98 else 99 return this.options[Object.keys(this.options)[0]].isDisabled(); 100 }; 101 102 this.add(options); 103 }; 104 105 exports.Option = function (name, text, icon, onClick, subOptions) { 106 this.name = name; 107 this.text = text; 108 this.icon = icon; 109 this.onClick = onClick; 110 this.subOptions = subOptions; 111 var option = this; 112 113 this.printIfAppEnabled = function (appName) { 114 this.appName = appName; 115 this.printIfNoApp = false; 116 } 117 118 this.enableIfAppEnabled = function (appName) { 119 this.appName = appName; 120 this.printIfNoApp = true; 121 } 122 123 this.generate = function () { 124 var a = $('<a>', { 125 'class': 'menu-option option-' + this.name.toLowerCase() 126 }).css('width', '100%'); 127 var iconSpan = $('<span>', { 128 'class': 'icon ' + this.icon 129 }); 130 var textSpan = $('<span>', { 131 'text': this.text, 132 }); 133 134 if (this.appName && !this.printIfNoApp && !exports.isAppAvailable(this.appName)) 135 return; 136 137 if (this.isDisabled()) { 138 a.attr('disabled', true).css({ 139 'cursor': 'default', 140 'background-color': '#AAA' 141 }); 142 143 iconSpan.css('cursor', 'default'); 144 textSpan.css('cursor', 'default'); 145 } 146 147 if (typeof this.onClick === 'string') { 148 a.on('click', function (event) { 149 event.stopPropagation(); 150 event.preventDefault(); 151 152 var $temp = $("<input>"); 153 $("body").append($temp); 154 $temp.val(option.onClick).select(); 155 document.execCommand("copy"); 156 $temp.remove(); 157 158 var html = textSpan.html(); 159 textSpan.html(t('files_rightclick', 'Copied !')); 160 161 setTimeout(function () { 162 textSpan.html(html); 163 }, 1000); 164 }); 165 } 166 else { 167 a.on('click', function (event) { 168 event.stopPropagation(); 169 event.preventDefault(); 170 171 exports.closeAllMenus(); 172 173 onClick(event, option); 174 }); 175 } 176 177 178 var li = $('<li>').append(a.append(iconSpan).append(textSpan)); 179 180 if (this.subOptions instanceof exports.Options && this.subOptions.getNbrOfOptions() > 0) { 181 var sub = $('<a>').append($('<span>').text('â¶') 182 .css('padding-right', '10px')).addClass(exports.selectors.arrowClass) 183 .attr('style', 'width: auto; padding-right: 0px !important'); 184 185 new exports.Menu(sub, this.subOptions, li).setAsSubMenu().setAlsoOnHover().setAlsoOnLeftClick(); 186 li.append(sub); 187 } 188 189 return li; 190 }; 191 192 this.isDisabled = function () { 193 if (this.appName && !exports.isAppAvailable(this.appName)) 194 return true; 195 196 return this.onClick === undefined; 197 }; 198 }; 199 200 exports.menus = []; 201 exports.Menu = function (delimiter, options, context, onClose, handledKeys) { 202 this.delimiter = $(delimiter); 203 this.context = context; 204 this.currentContext = undefined; 205 this.options = options || new exports.Options(); 206 this.onClose = onClose; 207 208 this.isSubMenu = false;
209 210 if (delimiter === undefined) 211 return undefined; 212 213 // Allow onClick function to access to menu data 214 var menu = this; 215 216 this.setContext = function (context) { 217 this.context = context; 218 219 return this; 220 }; 221 222 this.isOpened = function () { 223 return this.element !== undefined; 224 } 225 226 this.handleKeys = function (handledKeys) { 227 this.handledKeys = {}; 228 handledKeys = handledKeys || {}; 229 230 for (var key in exports.handableKeys) { 231 var keyName = exports.handableKeys[key]; 232 233 this.handledKeys[keyName] = handledKeys[keyName] || false; 234 } 235 }; 236 this.handleKeys(handledKeys); 237 238 var onClick = function (event, originalEvent) { 239 for (var key in exports.handableKeys) { 240 var keyName = exports.handableKeys[key]; 241 var targetedEvent = originalEvent || event; 242 243 if (targetedEvent[keyName + 'Key'] && !menu.handledKeys[keyName]) { 244 return true; 245 } 246 } 247 248 event.stopPropagation(); 249 event.preventDefault(); 250 251 if (originalEvent) { 252 event.clientX = originalEvent.clientX; 253 event.clientY = originalEvent.clientY; 254 } 255 256 var delimiter = $(this); 257 var context = menu.context; 258 var options = menu.options; 259 var params = menu.params; 260 261 if (menu.isSubMenu) { 262 if (!exports.closeAllSubMenus()) { 263 return !exports.clean(); 264 } 265 } 266 else if (!exports.closeAllMenus()) { 267 return !exports.clean(); 268 } 269 270 if (menu.isOpened()) { 271 return !exports.clean(); 272 } 273 274 exports.prepare(); 275 276 menu.attachedEvent = event; 277 278 if (typeof context === "function") 279 context = context(event); 280 281 context = (context === undefined) ? delimiter : $(context[0]); 282 menu.currentContext = context; 283 284 if (typeof options === "function") 285 options = options(event, context, delimiter); 286 287 if (options.getNbrOfOptions() === 0) { 288 return !exports.clean(); 289 } 290 291 var className = exports.selectors.menuClass + ' bubble open'; 292 293 menu.element = $('<div>', menu.isSubMenu ? { 294 'class': exports.selectord.subMenuClass + ' ' + className 295 } : { 296 'id': exports.selectors.menuId, 297 'class': className 298 }).append(options.generate()); 299 300 menu.element.appendTo(exports.container); 301 302 if (menu.isSubMenu) { 303 var menuPosition = context.parents('.rightClick').first().position(); 304 var contextPosition = context.position(); 305 var top = menuPosition.top - contextPosition.top; 306 var left = menuPosition.left + context.width() + 2; 307 } 308 else { 309 var top = event.clientY - parseInt(menu.element.css("marginTop").replace('px', '')); 310 var left = event.clientX - parseInt(menu.element.css("marginLeft").replace('px', '')); 311 } 312 313 var height = menu.element.outerHeight(); 314 var width = menu.element.outerWidth(); 315 316 if (left + width >= $(window).width()) { 317 left -= width; 318 } 319 if (top + height >= $(window).height()) { 320 top -= height; 321 } 322 323 if (top < 0) { 324 top = 0; 325 } 326 if (left < 0) { 327 left = 0; 328 } 329 330 menu.element.css({ 331 'top': top, 332 'left': left, 333 'right': 'auto', 334 }); 335 336 var optionsDisabled = options.isDisabled(); 337 338 if (optionsDisabled) 339 menu.element.css('background-color', '#AAA'); 340 341 menu.element.on('contextmenu', function () { 342 return false;
343 }); 344 menu.element.on('mouseleave', function (event) { 345 if (menu.isOpenedOnHover) 346 menu.close(); 347 }); 348 349 return false; 350 }; 351 352 this.close = function () { 353 if (!this.isOpened()) 354 return true; 355 356 if (this.element) { 357 if (this.onClose) { 358 if (this.onClose(this.attachedEvent, this.currentContext, this.delimiter) === false) 359 return false; 360 } 361 362 this.element.remove(); 363 delete this.element; 364 365 exports.clean(); 366 } 367 368 return true; 369 }; 370 371 this.setAlsoOnLeftClick = function () { 372 this.delimiter.on('click', function (event) { 373 if (menu.isOpened() && !menu.isOpenedOnHover) { 374 menu.close(); 375 376 return false; 377 } 378 379 menu.isOpenedOnHover = false; 380 }).on('click', onClick); 381 382 return this; 383 }; 384 385 this.setAlsoOnHover = function () { 386 this.delimiter.on('mouseenter', function (event) { 387 menu.isOpenedOnHover = true; 388 }).on('mouseenter', onClick); 389 390 return this; 391 }; 392 393 this.setAsSubMenu = function (isSubMenu) { 394 this.isSubMenu = isSubMenu || true; 395 396 return this; 397 }; 398 399 this.delimiter.contextmenu(onClick); 400 exports.menus.push(this); 401 }; 402 403 exports.closeAllMenus = function () { 404 for (var key in exports.menus) { 405 if (exports.menus.hasOwnProperty(key)) { 406 if (exports.menus[key].close() === false) 407 return false; 408 } 409 } 410 411 return true; 412 }; 413 414 exports.closeAllSubMenus = function () { 415 for (var key in exports.menus) { 416 if (exports.menus.hasOwnProperty(key)) { 417 if (exports.menus[key].isSubMenu) { 418 if (exports.menus[key].close() === false) 419 return false; 420 } 421 } 422 } 423 424 return true; 425 }; 426 427 exports.isAMenuOpened = function ()Â { 428 for (var key = 0; key < exports.menus.length; key++) { 429 if (exports.menus[key].isOpened()) { 430 return true; 431 } 432 } 433 434 return false; 435 }; 436 437 exports.prepare = function () { 438 if (!exports.isAMenuOpened()) { 439 $(window).on('resize', exports.closeAllMenus); 440 $('body').addClass(exports.selectors.openedClass); 441 } 442 443 $('#' + exports.selectors.detectorId).css('display', 'block'); 444 } 445 446 exports.onKeyUp = function (event) { 447 event = event || window.event; 448 var isEscape = false;
449 450 if ("key" in event) { 451 isEscape = (event.key == "Escape" || event.key == "Esc"); 452 } else { 453 isEscape = (event.keyCode === 27); 454 } 455 456 if (isEscape) { 457 if (exports.isAMenuOpened()) { 458 exports.closeAllMenus(); 459 460 event.stopPropagation(); 461 } 462 } 463 }; 464 465 exports.clean = function ()Â { 466 if (!exports.isAMenuOpened()) { 467 $(window).off('resize', exports.closeAllMenus); 468 $('body').removeClass(exports.selectors.openedClass); 469 $('#' + exports.selectors.detectorId).css('display', 'none'); 470 471 return true; 472 } 473 474 return false; 475 }; 476 477 exports.propagateRightClick = function (event) { 478 exports.closeAllMenus(); 479 event.preventDefault(); 480 event.stopPropagation(); 481 482 $(document.elementFromPoint(event.clientX, event.clientY)).trigger('contextmenu', event); 483 } 484 485 exports.container = $('<div id="' + exports.selectors.containerId + '"></div>').appendTo('body'); 486 exports.detector = $('<div id="' + exports.selectors.detectorId + '"></div>').appendTo('body'); 487 488 exports.detector.on('click resize', exports.closeAllMenus); 489 exports.detector.on('contextmenu', exports.propagateRightClick); 490 491 document.onkeyup = exports.onKeyUp; 492})(window, jQuery, RightClick);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.