/** * ***** BEGIN LICENSE BLOCK ***** * This file is part of CustomStyle. * * CustomStyle is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * CustomStyle is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CustomStyle; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ***** END LICENSE BLOCK ***** * * Allows to customize page css per user (including anonymous ones) * Version 0.3.0 * */ var CustomStyle = { _CustomStyle_ : '', // used to detect self-context extPath : '', uiPath : '', // an array of current style controllers scs : [], formatMessage : function() { var args = [ 'customstyle_js_' ]; for ( var i = 0; i < arguments.length; i++ ) { args.push( arguments[i] ); } return MWutils.formatMessage.apply( MWutils, args ); }, // basename prefix of user's cookies cookiePrefix : '', /* set extension's cookie prefix so it won't conflict with cookies set by another extensions */ setCookiePrefix : function( name ) { this.cookiePrefix = name; }, getCookie : function ( cookieName ) { return MWutils.getCookie( this.cookiePrefix + cookieName ); }, setCookie : function( cookieName, value, expires, path, domain, secure ) { return MWutils.setCookie( this.cookiePrefix + cookieName, value, expires, path, domain, secure ); }, /* * onload hook */ init : function() { // switch to local context if ( typeof this._CustomStyle_ === 'undefined' ) { CustomStyle.init.call( CustomStyle ); return; } // wiki page content DOM object var content = document.getElementById( 'contentbody' ); if ( content === null ) { return; } var username = ( (wgUserName === null) ? 'anonymous' : md5lib.hex( wgUserName ) ); this.setCookiePrefix( wgDBname + '_wiki_CustomStyle_' + username + '_' ); this.scs.push( new StyleController( { proto : { 'checkEvent': 'checkEventCtrlOnly' }, node: content, // element to apply the style styleAttr: 'fontSize', // css attr cookieVar: 'fontsize', // cookie var name sliderId: 'fontsize-slider', // jQuery.ui slider id displayValueId: 'printFontSize', // span id to display numeric value valueMessageKey : 'selected_font_size', current: 100, min: 100, max: 152, step: 4 } ) ); // currently (april 2011) next controller does not work in Opera / FF if ( ! /^mozilla|opera$/.test( MWutils.browser.name ) ) { this.scs.push( new StyleController( { proto : { 'checkEvent': 'checkEventCtrlShift' }, node: document.body, // element to apply the style styleAttr: 'zoom', // css attr cookieVar: 'zoom', // cookie var name sliderId: 'zoom-slider', // jQuery.ui slider id displayValueId: 'printZoom', // span id to display numeric value valueMessageKey : 'selected_zoom', current: 100, min: 100, max: 152, step: 4 } ) ); } this.scsIterate( 'applyAttr' ); var mouseWheelEvt = ( /Firefox/i.test( navigator.userAgent ) ) ? 'DOMMouseScroll' : 'mousewheel'; MWutils.addEvent( document, mouseWheelEvt, CustomStyle.wheelEventHandler ); }, scsIterate : function() { var i, fn = arguments[0]; var args = []; var r, result = []; for ( i = 1; i < arguments.length; i++ ) { args.push( arguments[i] ); } for ( i = 0; i < this.scs.length; i++ ) { r = this.scs[i][fn].apply( this.scs[i], args ); if ( typeof r !== 'undefined' ) { result[i] = r; } } return result; }, wheelEventHandler : function( event ) { var evt = window.event || event; if ( !evt.ctrlKey ) { return true; } CustomStyle.scsIterate( 'checkEvent', evt ); //delta returns +120 when wheel is scrolled up, -120 when scrolled down var delta = evt.detail ? evt.detail * (-120) : evt.wheelDelta; CustomStyle.scsIterate( 'setByWheel', delta ); //disable default wheel action of scrolling page if ( evt.preventDefault ) { evt.preventDefault(); } else { evt.returnValue = false; } return false; }, /* * called via custom personal url */ setStyle : function() { // turn off office skin menu (when available) if ( typeof off == 'function' ) { off(); } if ( document.getElementById( 'styledialog' ) === null ) { // there is no DOM added for style dialog, this means jQuery has to be loaded this.bootstrap(); return; } // style dialog DOM exists, this means jQuery was loaded and dialog already was created // avoid to create the dialog twice - this leads to erroneous behaviour this.scsIterate( 'applyToStyleDialog' ); }, /* * load jQuery and most of jQuery ui via LABjs loader */ bootstrap : function() { this.extPath = wgScriptPath+'/extensions/CustomStyle/'; this.uiPath = this.extPath + 'jquery.ui/'; $LAB .script( this.extPath + 'jquery-1.4.4.min.js' ).wait() .script( this.uiPath + 'jquery.ui.core.js' ) .script( this.uiPath + 'jquery.ui.position.js' ) .script( this.uiPath + 'jquery.ui.widget.js' ).wait() .script( this.uiPath + 'jquery.ui.button.js' ) .script( this.uiPath + 'jquery.ui.mouse.js' ).wait() .script( this.uiPath + 'jquery.ui.draggable.js' ) .script( this.uiPath + 'jquery.ui.resizable.js' ) .script( this.uiPath + 'jquery.ui.slider.js' ).wait() .script( this.uiPath + 'jquery.ui.dialog.js' ).wait( CustomStyle.bootstrap2() ); }, bootstrap2 : function() { // switch to local context if ( typeof this._CustomStyle_ === 'undefined' ) { CustomStyle.bootstrap2.call( CustomStyle ); return; } // wait for jQuery and jQuery ui dialog to be loaded if ( typeof jQuery === 'undefined' || typeof jQuery(document).dialog === 'undefined' ) { window.setTimeout( CustomStyle.bootstrap2, 300 ); return; } $j = jQuery; this.createStyleDialog(); this.scsIterate( 'applyToStyleDialog' ); }, createStyleDialog : function() { // do not add the same dom twice if ( document.getElementById( 'styledialog' ) === null ) { // dom for style dialog and style change slider(s) // todo: modular generation? $j(document.body).append( '' ); } var styleDialog = $j( '#styledialog' ); this.scsIterate( 'createSlider' ); var styleDialogButtons = {}; // set localized button titles, thus defining these separately of dialog, with [] syntax styleDialogButtons[CustomStyle.formatMessage( 'button_cancel' )] = function() { $(this).dialog( 'close' ); }; styleDialogButtons[CustomStyle.formatMessage( 'button_ok' )] = function() { CustomStyle.scsIterate( 'storeDesiredAttr' ); $(this).dialog( 'close' ); }; styleDialog.dialog({ autoOpen: false, closeOnEscape: false, resizable: false, buttons: styleDialogButtons, open : function() { styleDialog.dialog( 'option' , 'title' , CustomStyle.formatMessage( 'dialog_style_title' ) ); }, close : function() { CustomStyle.scsIterate( 'setAttr' ); } }); }, }; /* currentFontSize: 100, minFontSize: 100, maxFontSize: 152, desiredFontSize: 100, */ function StyleController( hashmap ) { // DOM node (which style will be changed) this.node = hashmap.node; // style attribute to change this.styleAttr = hashmap.styleAttr; this.cookieVar = hashmap.cookieVar; // values in percents this.current = hashmap.current; this.min = hashmap.min; this.max = hashmap.max; this.step = hashmap.step; // modified in dialog this.desired = hashmap.current; // used in dialog this.sliderId = hashmap.sliderId; this.displayValueId = hashmap.displayValueId; this.valueMessageKey = hashmap.valueMessageKey; if ( typeof hashmap.proto === 'undefined' ) { return; } // key of hashmap.proto is polymorph; value is prototype for ( var poly in hashmap.proto ) { if ( typeof StyleController.prototype[hashmap.proto[poly]] === 'undefined' ) { alert( 'Unknown polymorphic prototype in new StyleController(): ' + hashmap.proto[poly] ); continue; } this[poly] = StyleController.prototype[hashmap.proto[poly]]; } } StyleController.prototype.checkEventCtrlOnly = function( evt ) { this.isOurEvent = evt.ctrlKey && !evt.shiftKey; } StyleController.prototype.checkEventCtrlShift = function( evt ) { this.isOurEvent = evt.ctrlKey && evt.shiftKey; } StyleController.prototype.setByWheel = function( delta ) { if ( !this.isOurEvent ) { return; } if ( delta <= -120 ) { this.current -= this.step; } else { this.current += this.step; } this.applyAttr( this.current ); // warning $j( '#'+this.sliderId ) will not return null when id is undefined, // use direct DOM instead if ( typeof $j === 'function' && document.getElementById( this.sliderId ) !== null ) { // set correct fontsize slider value $j( '#'+this.sliderId ).slider( 'value', this.current ); // display correct fontsize slider value $j( '#'+this.sliderId ).trigger( 'slide' ); } this.storeAttr(); } /* * apply font size from argument or from cookie, if the value was previousely stored there */ StyleController.prototype.applyAttr = function( val ) { var currentPref = (typeof val === 'undefined') ? parseInt( CustomStyle.getCookie( this.cookieVar ) ) : val; if ( isNaN( currentPref ) ) { currentPref = 100; } if ( currentPref < this.min ) { currentPref = this.min; } if ( currentPref > this.max ) { currentPref = this.max; } this.current = currentPref; this.setAttr( currentPref ); } /* * show attribute value visually either from argument or from this.current when no agrument given */ StyleController.prototype.setAttr = function( val ) { if ( typeof val === 'undefined' ) { val = this.current; } this.desired = val; // apply font size in percents to wiki article content style if ( this.node !== null ) { this.node.style[this.styleAttr] = val + '%'; } // show font size value in percents in dialog (when dialog was open) var span = document.getElementById( this.displayValueId ); if ( span !== null ) { span.innerHTML = val + '%'; } } /* * store font size in a user's cookie */ StyleController.prototype.storeAttr = function( val ) { if ( typeof val === 'undefined' ) { val = this.current; } else { this.current = val; } CustomStyle.setCookie( this.cookieVar, val, 0, '/' ); } StyleController.prototype.storeDesiredAttr = function() { this.storeAttr( this.desired ); } StyleController.prototype.getDialogHtml = function() { return '
' + CustomStyle.formatMessage( this.valueMessageKey, '' + this.current + '%' ) + '
'; } StyleController.prototype.applyToStyleDialog = function() { // set correct fontsize slider value $j( '#'+this.sliderId ).slider( 'value', this.current ); // display correct fontsize slider value $j( '#'+this.sliderId ).trigger( 'slide' ); // open already created dialog $j( '#styledialog' ).dialog( 'open' ); // make sure fontsize is displayed accordingly to current settings // (fixes re-opening of this window via personal url without having to close already opened window) this.setAttr( this.current ); } StyleController.prototype.createSlider = function() { var s = $j( '#' + this.sliderId ); var self = this; s.slider({ value: this.current, min: this.min, max: this.max, slide: function( event, ui ) { self.setAttr.call( self, ui.value ); } }); } onloadFuncts.push( CustomStyle.init );