Merge "(bug 25830) fix JS preview causing the page to "jump""
[lhc/web/wiklou.git] / skins / common / preview.js
1 /**
2 * Live preview script for MediaWiki
3 */
4 (function( $ ) {
5 window.doLivePreview = function( e ) {
6 var previewShowing = false;
7
8 e.preventDefault();
9
10 $( mw ).trigger( 'LivePreviewPrepare' );
11
12 var $wikiPreview = $( '#wikiPreview' );
13
14 $( '#mw-content-text' ).css( 'position', 'relative' );
15
16 if ( $wikiPreview.is( ':visible' ) ) {
17 previewShowing = true;
18 }
19
20 // show #wikiPreview if it's hidden (if it is hidden, it's also empty, so nothing changes in the rendering)
21 // to be able to scroll to it
22 $wikiPreview.show();
23
24 // Jump to where the preview will appear
25 $wikiPreview[0].scrollIntoView();
26
27 var postData = $('#editform').formToArray();
28 postData.push( { 'name' : e.target.name, 'value' : '1' } );
29
30 // Hide active diff, used templates, old preview if shown
31 var copyElements = ['#wikiPreview', '#wikiDiff', '.templatesUsed', '.hiddencats',
32 '#catlinks', '#p-lang', '.mw-summary-preview'];
33 var copySelector = copyElements.join(',');
34
35 $.each( copyElements, function( k, v ) {
36 $( v ).fadeTo( 'fast', 0.4 );
37 } );
38
39 // Display a loading graphic
40 var loadSpinner = $('<div class="mw-ajax-loader"/>');
41 // Move away from header (default is -16px)
42 loadSpinner.css( 'top', '0' );
43
44 // If the preview is already showing, overlay the spinner on top of it.
45 if ( previewShowing ) {
46 loadSpinner.css( {
47 'position': 'absolute',
48 'z-index': '3',
49 'left': '50%',
50 'margin-left': '-16px'
51 } );
52 }
53 $wikiPreview.before( loadSpinner );
54
55 var page = $('<div/>');
56 var target = $('#editform').attr('action');
57
58 if ( !target ) {
59 target = window.location.href;
60 }
61
62 page.load( target + ' ' + copySelector, postData,
63 function() {
64
65 for( var i=0; i<copyElements.length; ++i) {
66 // For all the specified elements, find the elements in the loaded page
67 // and the real page, empty the element in the real page, and fill it
68 // with the content of the loaded page
69 var copyContent = page.find( copyElements[i] ).contents();
70 $(copyElements[i]).empty().append( copyContent );
71 var newClasses = page.find( copyElements[i] ).prop('class');
72 $(copyElements[i]).prop( 'class', newClasses );
73 }
74
75 $.each( copyElements, function( k, v ) {
76 // Don't belligerently show elements that are supposed to be hidden
77 $( v ).fadeTo( 'fast', 1, function() {
78 $( this ).css( 'display', '' );
79 } );
80 } );
81
82 loadSpinner.remove();
83
84 $( mw ).trigger( 'LivePreviewDone', [copyElements] );
85 } );
86 };
87
88 // Shamelessly stolen from the jQuery form plugin, which is licensed under the GPL.
89 // http://jquery.malsup.com/form/#download
90 $.fn.formToArray = function() {
91 var a = [];
92 if (this.length == 0) return a;
93
94 var form = this[0];
95 var els = form.elements;
96 if (!els) return a;
97 for(var i=0, max=els.length; i < max; i++) {
98 var el = els[i];
99 var n = el.name;
100 if (!n) continue;
101
102 var v = $.fieldValue(el, true);
103 if (v && v.constructor == Array) {
104 for(var j=0, jmax=v.length; j < jmax; j++)
105 a.push({name: n, value: v[j]});
106 }
107 else if (v !== null && typeof v != 'undefined')
108 a.push({name: n, value: v});
109 }
110
111 if (form.clk) {
112 // input type=='image' are not found in elements array! handle it here
113 var $input = $(form.clk), input = $input[0], n = input.name;
114 if (n && !input.disabled && input.type == 'image') {
115 a.push({name: n, value: $input.val()});
116 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
117 }
118 }
119 return a;
120 };
121
122 /**
123 * Returns the value of the field element.
124 */
125 $.fieldValue = function(el, successful) {
126 var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
127 if (typeof successful == 'undefined') successful = true;
128
129 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
130 (t == 'checkbox' || t == 'radio') && !el.checked ||
131 (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
132 tag == 'select' && el.selectedIndex == -1))
133 return null;
134
135 if (tag == 'select') {
136 var index = el.selectedIndex;
137 if (index < 0) return null;
138 var a = [], ops = el.options;
139 var one = (t == 'select-one');
140 var max = (one ? index+1 : ops.length);
141 for(var i=(one ? index : 0); i < max; i++) {
142 var op = ops[i];
143 if (op.selected) {
144 var v = op.value;
145 if (!v) // extra pain for IE...
146 v = (op.attributes && op.attributes['value'] &&
147 !(op.attributes['value'].specified))
148 ? op.text : op.value;
149 if (one) return v;
150 a.push(v);
151 }
152 }
153 return a;
154 }
155 return el.value;
156 };
157
158 $(document).ready( function() {
159 // construct space for interwiki links if missing
160 // (it is usually not shown when action=edit, but shown if action=submit)
161 if ( !document.getElementById( 'p-lang' ) && document.getElementById( 'p-tb' ) ) {
162 // we need not hide this, because it's empty anyway
163 $( '#p-tb' ).after( $( '<div>' ).attr( 'id', 'p-lang' ) );
164 }
165
166 // construct space for summary preview if missing
167 if ( $( '.mw-summary-preview' ).length === 0 ) {
168 $( '.editCheckboxes' ).before( $( '<div>' ).addClass( 'mw-summary-preview' ) );
169 }
170
171 // construct space for diff if missing. also load diff styles.
172 if ( !document.getElementById( 'wikiDiff' ) && document.getElementById( 'wikiPreview' ) ) {
173 $( '#wikiPreview' ).after( $( '<div>' ).attr( 'id', 'wikiDiff' ) );
174 // diff styles are by default only loaded when needed
175 // if there was no diff container, we can expect the styles not to be there either
176 mw.loader.load( 'mediawiki.action.history.diff' );
177 }
178
179 $( '#wpPreview, #wpDiff' ).click( doLivePreview );
180 } );
181 }) ( jQuery );