User:Jack who built the house/referenceTooltips.js
Appearance
From Wikipedia, the free encyclopedia
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump.
This code will be executed when previewing this page.
This code will be executed when previewing this page.
Documentation for this user script can be added at User:Jack who built the house/referenceTooltips.
mw.util.addCSS('\ /* See [[mw:Reference Tooltips]] */\ \ .rt-tooltip {\ position: absolute;\ z-index: 100;\ max-width: 350px;\ background: #fff;\ color: #222;\ font-size: 13px;\ line-height: 1.5em;\ border: 1px solid #c8ccd1;\ border-radius: 3px;\ -webkit-box-shadow: 0 15px 45px -10px rgba(0, 0, 0, 0.3);\ box-shadow: 0 15px 45px -10px rgba(0, 0, 0, 0.3);\ overflow-wrap: break-word;\ }\ \ .rt-tooltip.rt-tooltip-insideWindow {\ z-index: 110;\ }\ \ .rt-tooltipContent {\ padding: 8px 11px;\ }\ \ .rt-tooltip-above .rt-tooltipContent {\ margin-bottom: -8px;\ padding-bottom: 16px;\ }\ \ .rt-tooltip-below .rt-tooltipContent {\ margin-top: -10px;\ padding-top: 18px;\ }\ \ .rt-tooltipTail,\ .rt-tooltipTail:after {\ position: absolute;\ width: 12px;\ height: 12px;\ }\ \ .rt-tooltipTail {\ background: #c8ccd1;\ background: -webkit-linear-gradient(bottom left, #c8ccd1 50%, rgba(0, 0, 0, 0) 50%);\ background: linear-gradient(to top right, #c8ccd1 50%, rgba(0, 0, 0, 0) 50%);\ }\ \ .rt-tooltipTail:after {\ content: "";\ background: #fff;\ bottom: 1px;\ left: 1px;\ }\ \ .rt-tooltip-above .rt-tooltipTail {\ -webkit-transform: rotate(-45deg);\ transform: rotate(-45deg);\ -webkit-transform-origin: 100% 100%;\ transform-origin: 100% 100%;\ bottom: 0;\ left: 15px;\ }\ \ .rt-tooltip-below .rt-tooltipTail {\ -webkit-transform: rotate(135deg);\ transform: rotate(135deg);\ -webkit-transform-origin: 0 0;\ transform-origin: 0 0;\ top: 0;\ left: 27px;\ }\ \ .rt-settingsLink {\ background-image: linear-gradient(transparent,transparent),url(data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%3E%0D%0A%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%0D%0A%20%20%20%20%3Cpath%20fill%3D%22%23555%22%20d%3D%22M20%2014.5v-2.9l-1.8-.3c-.1-.4-.3-.8-.6-1.4l1.1-1.5-2.1-2.1-1.5%201.1c-.5-.3-1-.5-1.4-.6L13.5%205h-2.9l-.3%201.8c-.5.1-.9.3-1.4.6L7.4%206.3%205.3%208.4l1%201.5c-.3.5-.4.9-.6%201.4l-1.7.2v2.9l1.8.3c.1.5.3.9.6%201.4l-1%201.5%202.1%202.1%201.5-1c.4.2.9.4%201.4.6l.3%201.8h3l.3-1.8c.5-.1.9-.3%201.4-.6l1.5%201.1%202.1-2.1-1.1-1.5c.3-.5.5-1%20.6-1.4l1.5-.3zM12%2016c-1.7%200-3-1.3-3-3s1.3-3%203-3%203%201.3%203%203-1.3%203-3%203z%22%2F%3E%0D%0A%3C%2Fsvg%3E);\ float: right;\ cursor: pointer;\ margin: -4px -4px 0 8px;\ height: 24px;\ width: 24px;\ border-radius: 2px;\ background-position: center center;\ background-repeat: no-repeat;\ background-size: 24px 24px;\ }\ \ .rt-settingsLink:hover {\ background-color: #eee;\ }\ \ .rt-target {\ background-color: #def;\ }\ \ .rt-enableSelect {\ font-weight: bold;\ }\ \ .rt-settingsFormSeparator {\ margin: 0.85714286em 0;\ }\ \ .rt-numberInput.rt-numberInput {\ width: 150px;\ }\ \ .rt-tooltipsForCommentsField.rt-tooltipsForCommentsField.rt-tooltipsForCommentsField {\ margin-top: 1.64285714em;\ }\ \ .rt-disabledHelp {\ border-collapse: collapse;\ }\ \ .rt-disabledHelp td {\ padding: 0;\ }\ \ .rt-disabledNote.rt-disabledNote {\ vertical-align: bottom;\ padding-left: 0.36em;\ font-weight: bold;\ }\ \ @-webkit-keyframes rt-fade-in-up {\ 0% {\ opacity: 0;\ -webkit-transform: translate(0, 20px);\ -moz-transform: translate(0, 20px);\ -ms-transform: translate(0, 20px);\ transform: translate(0, 20px)\ }\ 100% {\ opacity: 1;\ -webkit-transform: translate(0, 0);\ -moz-transform: translate(0, 0);\ -ms-transform: translate(0, 0);\ transform: translate(0, 0)\ }\ }\ \ @-moz-keyframes rt-fade-in-up {\ 0% {\ opacity: 0;\ -webkit-transform: translate(0, 20px);\ -moz-transform: translate(0, 20px);\ -ms-transform: translate(0, 20px);\ transform: translate(0, 20px)\ }\ 100% {\ opacity: 1;\ -webkit-transform: translate(0, 0);\ -moz-transform: translate(0, 0);\ -ms-transform: translate(0, 0);\ transform: translate(0, 0)\ }\ }\ \ @keyframes rt-fade-in-up {\ 0% {\ opacity: 0;\ -webkit-transform: translate(0, 20px);\ -moz-transform: translate(0, 20px);\ -ms-transform: translate(0, 20px);\ transform: translate(0, 20px)\ }\ 100% {\ opacity: 1;\ -webkit-transform: translate(0, 0);\ -moz-transform: translate(0, 0);\ -ms-transform: translate(0, 0);\ transform: translate(0, 0)\ }\ }\ \ @-webkit-keyframes rt-fade-in-down {\ 0% {\ opacity: 0;\ -webkit-transform: translate(0, -20px);\ -moz-transform: translate(0, -20px);\ -ms-transform: translate(0, -20px);\ transform: translate(0, -20px)\ }\ 100% {\ opacity: 1;\ -webkit-transform: translate(0, 0);\ -moz-transform: translate(0, 0);\ -ms-transform: translate(0, 0);\ transform: translate(0, 0)\ }\ }\ \ @-moz-keyframes rt-fade-in-down {\ 0% {\ opacity: 0;\ -webkit-transform: translate(0, -20px);\ -moz-transform: translate(0, -20px);\ -ms-transform: translate(0, -20px);\ transform: translate(0, -20px)\ }\ 100% {\ opacity: 1;\ -webkit-transform: translate(0, 0);\ -moz-transform: translate(0, 0);\ -ms-transform: translate(0, 0);\ transform: translate(0, 0)\ }\ }\ \ @keyframes rt-fade-in-down {\ 0% {\ opacity: 0;\ -webkit-transform: translate(0, -20px);\ -moz-transform: translate(0, -20px);\ -ms-transform: translate(0, -20px);\ transform: translate(0, -20px)\ }\ 100% {\ opacity: 1;\ -webkit-transform: translate(0, 0);\ -moz-transform: translate(0, 0);\ -ms-transform: translate(0, 0);\ transform: translate(0, 0)\ }\ }\ \ @-webkit-keyframes rt-fade-out-down {\ 0% {\ opacity: 1;\ -webkit-transform: translate(0, 0);\ -moz-transform: translate(0, 0);\ -ms-transform: translate(0, 0);\ transform: translate(0, 0)\ }\ 100% {\ opacity: 0;\ -webkit-transform: translate(0, 20px);\ -moz-transform: translate(0, 20px);\ -ms-transform: translate(0, 20px);\ transform: translate(0, 20px)\ }\ }\ \ @-moz-keyframes rt-fade-out-down {\ 0% {\ opacity: 1;\ -webkit-transform: translate(0, 0);\ -moz-transform: translate(0, 0);\ -ms-transform: translate(0, 0);\ transform: translate(0, 0)\ }\ 100% {\ opacity: 0;\ -webkit-transform: translate(0, 20px);\ -moz-transform: translate(0, 20px);\ -ms-transform: translate(0, 20px);\ transform: translate(0, 20px)\ }\ }\ \ @keyframes rt-fade-out-down {\ 0% {\ opacity: 1;\ -webkit-transform: translate(0, 0);\ -moz-transform: translate(0, 0);\ -ms-transform: translate(0, 0);\ transform: translate(0, 0)\ }\ 100% {\ opacity: 0;\ -webkit-transform: translate(0, 20px);\ -moz-transform: translate(0, 20px);\ -ms-transform: translate(0, 20px);\ transform: translate(0, 20px)\ }\ }\ \ @-webkit-keyframes rt-fade-out-up {\ 0% {\ opacity: 1;\ -webkit-transform: translate(0, 0);\ -moz-transform: translate(0, 0);\ -ms-transform: translate(0, 0);\ transform: translate(0, 0)\ }\ 100% {\ opacity: 0;\ -webkit-transform: translate(0, -20px);\ -moz-transform: translate(0, -20px);\ -ms-transform: translate(0, -20px);\ transform: translate(0, -20px)\ }\ }\ \ @-moz-keyframes rt-fade-out-up {\ 0% {\ opacity: 1;\ -webkit-transform: translate(0, 0);\ -moz-transform: translate(0, 0);\ -ms-transform: translate(0, 0);\ transform: translate(0, 0)\ }\ 100% {\ opacity: 0;\ -webkit-transform: translate(0, -20px);\ -moz-transform: translate(0, -20px);\ -ms-transform: translate(0, -20px);\ transform: translate(0, -20px)\ }\ }\ \ @keyframes rt-fade-out-up {\ 0% {\ opacity: 1;\ -webkit-transform: translate(0, 0);\ -moz-transform: translate(0, 0);\ -ms-transform: translate(0, 0);\ transform: translate(0, 0)\ }\ 100% {\ opacity: 0;\ -webkit-transform: translate(0, -20px);\ -moz-transform: translate(0, -20px);\ -ms-transform: translate(0, -20px);\ transform: translate(0, -20px)\ }\ }\ \ .rt-fade-in-up {\ -webkit-animation: rt-fade-in-up 0.2s ease forwards;\ -moz-animation: rt-fade-in-up 0.2s ease forwards;\ animation: rt-fade-in-up 0.2s ease forwards\ }\ \ .rt-fade-in-down {\ -webkit-animation: rt-fade-in-down 0.2s ease forwards;\ -moz-animation: rt-fade-in-down 0.2s ease forwards;\ animation: rt-fade-in-down 0.2s ease forwards\ }\ \ .rt-fade-out-down {\ -webkit-animation: rt-fade-out-down 0.2s ease forwards;\ -moz-animation: rt-fade-out-down 0.2s ease forwards;\ animation: rt-fade-out-down 0.2s ease forwards\ }\ \ .rt-fade-out-up {\ -webkit-animation: rt-fade-out-up 0.2s ease forwards;\ -moz-animation: rt-fade-out-up 0.2s ease forwards;\ animation: rt-fade-out-up 0.2s ease forwards\ }\ '); // See [[mw:Reference Tooltips]] (function(){ // enwiki settings varREF_LINK_SELECTOR='.reference, a[href^="#CITEREF"]', COMMENTED_TEXT_CLASS='', COMMENTED_TEXT_SELECTOR=(COMMENTED_TEXT_CLASS?'.'+COMMENTED_TEXT_CLASS+', ':'')+ 'abbr[title]'; mw.messages.set({ 'rt-settings':'Reference Tooltips settings', 'rt-enable-footer':'Enable Reference Tooltips', 'rt-settings-title':'Reference Tooltips', 'rt-save':'Save', 'rt-cancel':'Cancel', 'rt-enable':'Enable', 'rt-disable':'Disable', 'rt-activationMethod':'Tooltip appears when', 'rt-hovering':'hovering', 'rt-clicking':'clicking', 'rt-delay':'Delay before the tooltip appears (in milliseconds)', 'rt-tooltipsForComments':'Show tooltips over <span title="Tooltip example" class="'+(COMMENTED_TEXT_CLASS||'rt-commentedText')+'" style="border-bottom: 1px dotted; cursor: help;">text with a dotted underline</span> in Reference Tooltips style (allows to see such tooltips on devices with no mouse support)', 'rt-disabledNote':'You can re-enable Reference Tooltips using a link in the footer of the page.', 'rt-done':'Done', 'rt-enabled':'Reference Tooltips are enabled' }); // "Global" variables varSECONDS_IN_A_DAY=60*60*24, CLASSES={ FADE_IN_DOWN:'rt-fade-in-down', FADE_IN_UP:'rt-fade-in-up', FADE_OUT_DOWN:'rt-fade-out-down', FADE_OUT_UP:'rt-fade-out-up' }, IS_TOUCHSCREEN='ontouchstart'indocument.documentElement, // Quite a rough check for mobile browsers, a mix of what is advised at // https://stackoverflow.com/a/24600597 (sends to // https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent) // and https://stackoverflow.com/a/14301832 IS_MOBILE=/Mobi|Android/i.test(navigator.userAgent)|| typeofwindow.orientation!=='undefined', CLIENT_NAME=$.client.profile().name, settingsString,settings,enabled,delay,activatedByClick,tooltipsForComments,cursorWaitCss, windowManager, $body=$(document.body), $window=$(window); functionrt($content){ // Popups gadget if(window.pg){ return; } varteSelector, settingsDialogOpening=false; functionsetSettingsCookie(){ mw.cookie.set( 'RTsettings', Number(enabled)+'|'+delay+'|'+Number(activatedByClick)+'|'+ Number(tooltipsForComments), {path:'/',expires:90*SECONDS_IN_A_DAY,prefix:''} ); } functionenableRt(){ enabled=true; setSettingsCookie(); $('.rt-enableItem').remove(); rt($content); mw.notify(mw.msg('rt-enabled')); } functiondisableRt(){ $content.find(teSelector).removeClass('rt-commentedText').off('.rt'); $body.off('.rt'); $window.off('.rt'); } functionaddEnableLink(){ // #footer-places – Vector // #f-list – Timeless, Monobook, Modern // parent of #footer li – Cologne Blue var$footer=$('#footer-places, #f-list'); if(!$footer.length){ $footer=$('#footer li').parent(); } $footer.append( $('<li>') .addClass('rt-enableItem') .append( $('<a>') .text(mw.msg('rt-enable-footer')) .attr('href','javascript:') .click(function(e){ e.preventDefault(); enableRt(); }) ) ); } functionTooltippedElement($element){ vartooltip, te=this; functiononStartEvent(e){ varshowRefArgs; if(activatedByClick){ e.preventDefault(); } if(!te.noRef){ showRefArgs=[$(this)]; if(te.type!=='supRef'){ showRefArgs.push(e.pageX,e.pageY); } te.showRef.apply(te,showRefArgs); } } functiononEndEvent(){ if(!te.noRef){ te.hideRef(); } } if(!$element){ return; } // TooltippedElement.$element and TooltippedElement.$originalElement will be different when // the first is changed after its cloned version is hovered in a tooltip this.$element=$element; this.$originalElement=$element; if(this.$element.is(REF_LINK_SELECTOR)){ if(this.$element.prop('tagName')==='SUP'){ this.type='supRef'; }else{ this.type='harvardRef'; } }else{ this.type='commentedText'; this.comment=this.$element.attr('title'); if(!this.comment){ return; } this.$element.addClass('rt-commentedText'); } this.$element.on(activatedByClick? {'click.rt':onStartEvent}: {'mouseenter.rt':onStartEvent,'mouseleave.rt':onEndEvent} ); this.hideRef=function(immediately){ clearTimeout(te.showTimer); if(this.type==='commentedText'){ this.$element.attr('title',this.comment); } if(this.tooltip&&this.tooltip.isPresent){ if(activatedByClick||immediately){ this.tooltip.hide(); }else{ this.hideTimer=setTimeout(function(){ te.tooltip.hide(); },200); } }elseif(this.$ref&&this.$ref.hasClass('rt-target')){ this.$ref.removeClass('rt-target'); if(activatedByClick){ $body.off('click.rt touchstart.rt',this.onBodyClick); } } }; this.showRef=function($element,ePageX,ePageY){ // Popups gadget if(window.pg){ disableRt(); return; } vartooltipInitiallyPresent=this.tooltip&&this.tooltip.isPresent; functionreallyShow(){ varviewportTop,refOffsetTop,teHref; if(!te.$ref&&!te.comment){ teHref=te.type==='supRef'? te.$element.find('a').attr('href'): te.$element.attr('href');// harvardRef te.$ref=teHref&& $('#'+$.escapeSelector(teHref.slice(1))); if(!te.$ref||!te.$ref.length||!te.$ref.text()){ te.noRef=true; return; } } if(!tooltipInitiallyPresent&&!te.comment){ viewportTop=$window.scrollTop(); refOffsetTop=te.$ref.offset().top; if(!activatedByClick&& viewportTop<refOffsetTop&& viewportTop+$window.height()>refOffsetTop+te.$ref.height()&& // There can be gadgets/scripts that make references horizontally scrollable. $window.width()>te.$ref.offset().left+te.$ref.width() ){ // Highlight the reference itself te.$ref.addClass('rt-target'); return; } } if(!te.tooltip){ te.tooltip=newTooltip(te); if(!te.tooltip.$content.length){ return; } } // If this tooltip is called from inside another tooltip. We can't define it // in the constructor since a ref can be cloned but have the same Tooltip object; // so, Tooltip.parent is a floating value. te.tooltip.parent=te.$element.closest('.rt-tooltip').data('tooltip'); if(te.tooltip.parent&&te.tooltip.parent.disappearing){ return; } te.tooltip.show(); if(tooltipInitiallyPresent){ if(te.tooltip.$element.hasClass('rt-tooltip-above')){ te.tooltip.$element.addClass(CLASSES.FADE_IN_DOWN); }else{ te.tooltip.$element.addClass(CLASSES.FADE_IN_UP); } return; } te.tooltip.calculatePosition(ePageX,ePageY); $window.on('resize.rt',te.onWindowResize); } // We redefine this.$element here because e.target can be a reference link inside // a reference tooltip, not a link that was initially assigned to this.$element this.$element=$element; if(this.type==='commentedText'){ this.$element.attr('title',''); } if(activatedByClick){ if(tooltipInitiallyPresent|| (this.$ref&&this.$ref.hasClass('rt-target')) ){ return; }else{ setTimeout(function(){ $body.on('click.rt touchstart.rt',te.onBodyClick); },0); } } if(activatedByClick||tooltipInitiallyPresent){ reallyShow(); }else{ this.showTimer=setTimeout(reallyShow,delay); } }; this.onBodyClick=function(e){ if(!te.tooltip&&!te.$ref.hasClass('rt-target')){ return; } var$current=$(e.target); functioncontextMatchesParameter(parameter){ returnthis===parameter; } // The last condition is used to determine cases when a clicked tooltip is the current // element's tooltip or one of its descendants while($current.length&& (!$current.hasClass('rt-tooltip')|| !$current.data('tooltip')|| !$current.data('tooltip').upToTopParent( contextMatchesParameter,[te.tooltip], true ) ) ){ $current=$current.parent(); } if(!$current.length){ te.hideRef(); } }; this.onWindowResize=function(){ te.tooltip.calculatePosition(); }; } functionTooltip(te){ functionopenSettingsDialog(){ varsettingsDialog,settingsWindow; if(cursorWaitCss){ cursorWaitCss.disabled=true; } functionSettingsDialog(){ SettingsDialog.parent.call(this); } OO.inheritClass(SettingsDialog,OO.ui.ProcessDialog); SettingsDialog.static.name='settingsDialog'; SettingsDialog.static.title=mw.msg('rt-settings-title'); SettingsDialog.static.actions=[ { modes:'basic', action:'save', label:mw.msg('rt-save'), flags:['primary','progressive'] }, { modes:'basic', label:mw.msg('rt-cancel'), flags:'safe' }, { modes:'disabled', action:'deactivated', label:mw.msg('rt-done'), flags:['primary','progressive'] } ]; SettingsDialog.prototype.initialize=function(){ vardialog=this; SettingsDialog.parent.prototype.initialize.apply(this,arguments); this.enableOption=newOO.ui.RadioOptionWidget({ label:mw.msg('rt-enable') }); this.disableOption=newOO.ui.RadioOptionWidget({ label:mw.msg('rt-disable') }); this.enableSelect=newOO.ui.RadioSelectWidget({ items:[this.enableOption,this.disableOption], classes:['rt-enableSelect'] }); this.enableSelect.selectItem(this.enableOption); this.enableSelect.on('choose',function(item){ if(item===dialog.disableOption){ dialog.activationMethodSelect.setDisabled(true); dialog.delayInput.setDisabled(true); dialog.tooltipsForCommentsCheckbox.setDisabled(true); }else{ dialog.activationMethodSelect.setDisabled(false); dialog.delayInput.setDisabled(dialog.clickOption.isSelected()); dialog.tooltipsForCommentsCheckbox.setDisabled(false); } }); this.hoverOption=newOO.ui.RadioOptionWidget({ label:mw.msg('rt-hovering') }); this.clickOption=newOO.ui.RadioOptionWidget({ label:mw.msg('rt-clicking') }); this.activationMethodSelect=newOO.ui.RadioSelectWidget({ items:[this.hoverOption,this.clickOption] }); this.activationMethodSelect.selectItem(activatedByClick? this.clickOption: this.hoverOption ); this.activationMethodSelect.on('choose',function(item){ if(item===dialog.clickOption){ dialog.delayInput.setDisabled(true); }else{ dialog.delayInput.setDisabled(dialog.clickOption.isSelected()); } }); this.activationMethodField=newOO.ui.FieldLayout(this.activationMethodSelect,{ label:mw.msg('rt-activationMethod'), align:'top' }); this.delayInput=newOO.ui.NumberInputWidget({ input:{value:delay}, step:50, min:0, max:5000, disabled:activatedByClick, classes:['rt-numberInput'] }); this.delayField=newOO.ui.FieldLayout(this.delayInput,{ label:mw.msg('rt-delay'), align:'top' }); this.tooltipsForCommentsCheckbox=newOO.ui.CheckboxInputWidget({ selected:tooltipsForComments }); this.tooltipsForCommentsField=newOO.ui.FieldLayout( this.tooltipsForCommentsCheckbox, { label:newOO.ui.HtmlSnippet(mw.msg('rt-tooltipsForComments')), align:'inline', classes:['rt-tooltipsForCommentsField'] } ); newTooltippedElement( this.tooltipsForCommentsField.$element.find( '.'+(COMMENTED_TEXT_CLASS||'rt-commentedText') ) ); this.fieldset=newOO.ui.FieldsetLayout(); this.fieldset.addItems([ this.activationMethodField, this.delayField, this.tooltipsForCommentsField ]); this.panelSettings=newOO.ui.PanelLayout({ padded:true, expanded:false }); this.panelSettings.$element.append( this.enableSelect.$element, $('<hr>').addClass('rt-settingsFormSeparator'), this.fieldset.$element ); this.panelDisabled=newOO.ui.PanelLayout({ padded:true, expanded:false }); this.panelDisabled.$element.append( $('<table>') .addClass('rt-disabledHelp') .append( $('<tr>').append( $('<td>').append( $('<img>').attr('src','https://en.wikipedia.org/w/load.php?modules=ext.popups.images&image=footer&format=rasterized&lang=ru&skin=vector&version=0uotisb') ), $('<td>') .addClass('rt-disabledNote') .text(mw.msg('rt-disabledNote')) ) ) ); this.stackLayout=newOO.ui.StackLayout({ items:[this.panelSettings,this.panelDisabled] }); this.$body.append(this.stackLayout.$element); }; SettingsDialog.prototype.getSetupProcess=function(data){ returnSettingsDialog.parent.prototype.getSetupProcess.call(this,data) .next(function(){ this.stackLayout.setItem(this.panelSettings); this.actions.setMode('basic'); },this); }; SettingsDialog.prototype.getActionProcess=function(action){ vardialog=this; if(action==='save'){ returnnewOO.ui.Process(function(){ varnewDelay=Number(dialog.delayInput.getValue()); enabled=dialog.enableOption.isSelected(); if(newDelay>=0&&newDelay<=5000){ delay=newDelay; } activatedByClick=dialog.clickOption.isSelected(); tooltipsForComments=dialog.tooltipsForCommentsCheckbox.isSelected(); setSettingsCookie(); if(enabled){ dialog.close(); disableRt(); rt($content); }else{ dialog.actions.setMode('disabled'); dialog.stackLayout.setItem(dialog.panelDisabled); disableRt(); addEnableLink(); } }); }elseif(action==='deactivated'){ dialog.close(); } returnSettingsDialog.parent.prototype.getActionProcess.call(this,action); }; SettingsDialog.prototype.getBodyHeight=function(){ returnthis.stackLayout.getCurrentItem().$element.outerHeight(true); }; tooltip.upToTopParent(functionadjustRightAndHide(){ if(this.isPresent){ if(this.$element[0].style.right){ this.$element.css( 'right', '+='+(window.innerWidth-$window.width()) ); } this.te.hideRef(true); } }); if(!windowManager){ windowManager=newOO.ui.WindowManager(); $body.append(windowManager.$element); } settingsDialog=newSettingsDialog(); windowManager.addWindows([settingsDialog]); settingsWindow=windowManager.openWindow(settingsDialog); settingsWindow.opened.then(function(){ settingsDialogOpening=false; }); settingsWindow.closed.then(function(){ windowManager.clearWindows(); }); } vartooltip=this; // This variable can change: one tooltip can be called from a harvard-style reference link // that is put into different tooltips this.te=te; switch(this.te.type){ case'supRef': this.id='rt-'+this.te.$originalElement.attr('id'); this.$content=this.te.$ref .children() .not('.mw-cite-backlink') .clone(true); break; case'harvardRef': this.id='rt-'+this.te.$originalElement.closest('li').attr('id'); this.$content=this.te.$ref .clone(true) .removeAttr('id'); break; case'commentedText': this.id='rt-'+String(Math.random()).slice(2); this.$content=$(document.createTextNode(this.te.comment)); break; } if(!this.$content.length){ return; } this.insideWindow=Boolean(this.te.$element.closest('.oo-ui-window').length); this.$element=$('<div>') .addClass('rt-tooltip') .attr('id',this.id) .attr('role','tooltip') .data('tooltip',this); if(this.insideWindow){ this.$element.addClass('rt-tooltip-insideWindow'); } // We need the $content interlayer here in order for the settings icon to have correct // margins this.$content=this.$content .wrapAll('<div>') .parent() .addClass('rt-tooltipContent') .appendTo(this.$element); if(!activatedByClick){ this.$element .mouseenter(function(){ if(!tooltip.disappearing){ tooltip.upToTopParent(function(){ this.show(); }); } }) .mouseleave(function(e){ // https://stackoverflow.com/q/47649442 workaround. Relying on relatedTarget // alone has pitfalls: when alt-tabbing, relatedTarget is empty too if(CLIENT_NAME!=='chrome'|| (!e.originalEvent|| e.originalEvent.relatedTarget!==null|| !tooltip.clickedTime|| $.now()-tooltip.clickedTime>50 ) ){ tooltip.upToTopParent(function(){ this.te.hideRef(); }); } }) .click(function(){ tooltip.clickedTime=$.now(); }); } if(!this.insideWindow){ $('<div>') .addClass('rt-settingsLink') .attr('title',mw.msg('rt-settings')) .click(function(){ if(settingsDialogOpening){ return; } settingsDialogOpening=true; if(mw.loader.getState('oojs-ui')!=='ready'){ if(cursorWaitCss){ cursorWaitCss.disabled=false; }else{ cursorWaitCss=mw.util.addCSS('body { cursor: wait; }'); } } mw.loader.using(['oojs','oojs-ui'],openSettingsDialog); }) .prependTo(this.$content); } // Tooltip tail element is inside tooltip content element in order for the tooltip // not to disappear when the mouse is above the tail this.$tail=$('<div>') .addClass('rt-tooltipTail') .prependTo(this.$element); this.disappearing=false; this.show=function(){ this.disappearing=false; clearTimeout(this.te.hideTimer); clearTimeout(this.te.removeTimer); this.$element .removeClass(CLASSES.FADE_OUT_DOWN) .removeClass(CLASSES.FADE_OUT_UP); if(!this.isPresent){ $body.append(this.$element); } this.isPresent=true; }; this.hide=function(){ vartooltip=this; tooltip.disappearing=true; if(tooltip.$element.hasClass('rt-tooltip-above')){ tooltip.$element .removeClass(CLASSES.FADE_IN_DOWN) .addClass(CLASSES.FADE_OUT_UP); }else{ tooltip.$element .removeClass(CLASSES.FADE_IN_UP) .addClass(CLASSES.FADE_OUT_DOWN); } tooltip.te.removeTimer=setTimeout(function(){ if(tooltip.isPresent){ tooltip.$element.detach(); tooltip.$tail.css('left',''); if(activatedByClick){ $body.off('click.rt touchstart.rt',tooltip.te.onBodyClick); } $window.off('resize.rt',tooltip.te.onWindowResize); tooltip.isPresent=false; } },200); }; this.calculatePosition=function(ePageX,ePageY){ varteElement,teOffsets,teOffset,tooltipTailOffsetX,tooltipTailLeft, offsetYCorrection=0; this.$tail.css('left',''); teElement=this.te.$element.get(0); if(ePageX!==undefined){ tooltipTailOffsetX=ePageX; teOffsets=teElement.getClientRects&& teElement.getClientRects()|| teElement.getBoundingClientRect(); if(teOffsets.length>1){ for(vari=teOffsets.length-1;i>=0;i--){ if(ePageY>=Math.round($window.scrollTop()+teOffsets[i].top)&& ePageY<=Math.round( $window.scrollTop()+teOffsets[i].top+teOffsets[i].height ) ){ teOffset=teOffsets[i]; } } } } if(!teOffset){ teOffset=teElement.getClientRects&& teElement.getClientRects()[0]|| teElement.getBoundingClientRect(); } teOffset={ top:$window.scrollTop()+teOffset.top, left:$window.scrollLeft()+teOffset.left, width:teOffset.width, height:teOffset.height }; if(!tooltipTailOffsetX){ tooltipTailOffsetX=(teOffset.left*2+teOffset.width)/2; } if(CLIENT_NAME==='msie'&&this.te.type==='supRef'){ offsetYCorrection=-Number( this.te.$element.parent().css('font-size').replace('px','') )/2; } this.$element.css({ top:teOffset.top-this.$element.outerHeight()-7+offsetYCorrection, left:tooltipTailOffsetX-20, right:'' }); // Is it squished against the right side of the page? if(this.$element.offset().left+this.$element.outerWidth()>$window.width()-1){ this.$element.css({ left:'', right:0 }); tooltipTailLeft=tooltipTailOffsetX-this.$element.offset().left-5; } // Is a part of it above the top of the screen? if(teOffset.top<this.$element.outerHeight()+$window.scrollTop()+6){ this.$element .removeClass('rt-tooltip-above') .addClass('rt-tooltip-below') .addClass(CLASSES.FADE_IN_UP) .css({ top:teOffset.top+teOffset.height+9+offsetYCorrection }); if(tooltipTailLeft){ this.$tail.css('left',(tooltipTailLeft+12)+'px'); } }else{ this.$element .removeClass('rt-tooltip-below') .addClass('rt-tooltip-above') .addClass(CLASSES.FADE_IN_DOWN) // A fix for cases when a tooltip shown once is then wrongly positioned when it // is shown again after a window resize. We just repeat what is above. .css({ top:teOffset.top-this.$element.outerHeight()-7+offsetYCorrection }); if(tooltipTailLeft){ // 12 is the tail element width/height this.$tail.css('left',tooltipTailLeft+'px'); } } }; // Run some function for all the tooltips up to the top one in a tree. Its context will be // the tooltip, while its parameters may be passed to Tooltip.upToTopParent as an array // in the second parameter. If the third parameter passed to ToolTip.upToTopParent is true, // the execution stops when the function in question returns true for the first time, // and ToolTip.upToTopParent returns true as well. this.upToTopParent=function(func,parameters,stopAtTrue){ varreturnValue, currentTooltip=this; do{ returnValue=func.apply(currentTooltip,parameters); if(stopAtTrue&&returnValue){ break; } }while(currentTooltip=currentTooltip.parent); if(stopAtTrue){ returnreturnValue; } }; } if(!enabled){ addEnableLink(); return; } teSelector=REF_LINK_SELECTOR; if(tooltipsForComments){ teSelector+=', '+COMMENTED_TEXT_SELECTOR; } $content.find(teSelector).each(function(){ newTooltippedElement($(this)); }); } settingsString=mw.cookie.get('RTsettings',''); if(settingsString){ settings=settingsString.split('|'); enabled=Boolean(Number(settings[0])); delay=Number(settings[1]); activatedByClick=Boolean(Number(settings[2])); // The forth value was added later, so we provide for a default value. See comments below // for why we use "IS_TOUCHSCREEN && IS_MOBILE". tooltipsForComments=settings[3]===undefined? IS_TOUCHSCREEN&&IS_MOBILE: Boolean(Number(settings[3])); }else{ enabled=true; delay=200; // Since the mobile browser check is error-prone, adding IS_MOBILE condition here would probably // leave cases where a user interacting with the browser using touches doesn't know how to call // a tooltip in order to switch to activation by click. Some touch-supporting laptop users // interacting by touch (though probably not the most popular use case) would not be happy too. activatedByClick=IS_TOUCHSCREEN; // Arguably we shouldn't convert native tooltips into gadget tooltips for devices that have // mouse support, even if they have touchscreens (there are laptops with touchscreens). // IS_TOUCHSCREEN check here is for reliability, since the mobile check is prone to false // positives. tooltipsForComments=IS_TOUCHSCREEN&&IS_MOBILE; } mw.hook('wikipage.content').add(rt); }());