Index: trunk/extensions/TimeZonePicker/TimeZonePicker.hooks.php |
— | — | @@ -0,0 +1,45 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * TimeZonePicker extension: hooks |
| 5 | + * @copyright 2011 Brion Vibber <brion@pobox.com> |
| 6 | + */ |
| 7 | + |
| 8 | +class TimeZonePickerHooks { |
| 9 | + /* Static Methods */ |
| 10 | + |
| 11 | + /** |
| 12 | + * BeforePageDisplay hook |
| 13 | + * |
| 14 | + * Adds the modules to the page |
| 15 | + * |
| 16 | + * @param $out OutputPage output page |
| 17 | + * @param $skin Skin current skin |
| 18 | + */ |
| 19 | + public static function beforePageDisplay( $out, $skin ) { |
| 20 | + $title = $out->getTitle(); |
| 21 | + if( $title->isSpecial( 'Preferences' ) ) { |
| 22 | + $out->addModules('ext.tzpicker'); |
| 23 | + $out->addInlineScript("window.mw_ext_tzpicker_ZoneInfo=" . |
| 24 | + FormatJson::encode(self::zoneInfo())); |
| 25 | + } |
| 26 | + return true; |
| 27 | + } |
| 28 | + |
| 29 | + /** |
| 30 | + * Return a set of timezone information relevant to this joyful stuff :D |
| 31 | + */ |
| 32 | + public static function zoneInfo() { |
| 33 | + $zones = array(); |
| 34 | + $now = date_create(); |
| 35 | + foreach( timezone_identifiers_list() as $tz ) { |
| 36 | + $zone = timezone_open( $tz ); |
| 37 | + |
| 38 | + $name = timezone_name_get( $zone ); |
| 39 | + $location = timezone_location_get( $zone ); |
| 40 | + $offset = timezone_offset_get( $zone, $now ) / 60; // convert seconds to minutes |
| 41 | + |
| 42 | + $zones[] = array('name' => $name, 'offset' => $offset, 'location' => $location); |
| 43 | + } |
| 44 | + return $zones; |
| 45 | + } |
| 46 | +} |
Index: trunk/extensions/TimeZonePicker/resources/world-1440.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/TimeZonePicker/resources/world-1440.png |
___________________________________________________________________ |
Added: svn:mime-type |
1 | 47 | + application/octet-stream |
Index: trunk/extensions/TimeZonePicker/resources/world-360.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/TimeZonePicker/resources/world-360.png |
___________________________________________________________________ |
Added: svn:mime-type |
2 | 48 | + application/octet-stream |
Index: trunk/extensions/TimeZonePicker/resources/world-720.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/TimeZonePicker/resources/world-720.png |
___________________________________________________________________ |
Added: svn:mime-type |
3 | 49 | + application/octet-stream |
Index: trunk/extensions/TimeZonePicker/resources/ext.tzpicker.css |
— | — | @@ -0,0 +1,76 @@ |
| 2 | +/** |
| 3 | + * TimeZonePicker extension |
| 4 | + * @copyright 2011 Brion Vibber <brion@pobox.com> |
| 5 | + */ |
| 6 | + |
| 7 | +#mw-tzpicker { |
| 8 | + width: 720px; |
| 9 | + height: 558px; |
| 10 | + overflow: auto; |
| 11 | + overflow-x: hidden; |
| 12 | +} |
| 13 | +#mw-tzpicker-map { |
| 14 | + position: relative; /* for the markers to do absolute against... */ |
| 15 | + /* |
| 16 | + width: 360px; |
| 17 | + height: 280px; |
| 18 | + background: url('world-360.png'); |
| 19 | + */ |
| 20 | + width: 720px; |
| 21 | + height: 558px; |
| 22 | + background: url('world-720.png'); |
| 23 | +} |
| 24 | +#mw-tzpicker-overlay { |
| 25 | + position: absolute; |
| 26 | + width: 720px; |
| 27 | + height: 558px; |
| 28 | + background: url('overlay-720.png'); |
| 29 | +} |
| 30 | + |
| 31 | +#mw-tzpicker-map.zoom { |
| 32 | + position: relative; /* for the markers to do absolute against... */ |
| 33 | + width: 720px; |
| 34 | + height: 1116px; |
| 35 | + background: url('world-1440.png') -360px 0px; |
| 36 | +} |
| 37 | +#mw-tzpicker-map.zoom #mw-tzpicker-overlay { |
| 38 | + position: absolute; |
| 39 | + width: 720px; |
| 40 | + height: 1116px; |
| 41 | + background: url('overlay-1440.png') -360px 0px; |
| 42 | +} |
| 43 | + |
| 44 | +.mw-tzpicker-label { |
| 45 | + position: absolute; |
| 46 | + color: white; |
| 47 | + background: black; |
| 48 | + z-index: 10000; |
| 49 | + padding: 0px 6px; |
| 50 | + |
| 51 | + -moz-border-radius: 4px; |
| 52 | + -webkit-border-radius: 4px; |
| 53 | + -ie-border-radius: 4px; |
| 54 | + -opera-border-radius: 4px; |
| 55 | + border-radius: 4px; |
| 56 | + |
| 57 | + box-shadow: 2px 2px 4px #888; |
| 58 | +} |
| 59 | +.mw-tzpicker-marker { |
| 60 | + position: absolute; |
| 61 | + width: 8px; |
| 62 | + height: 8px; |
| 63 | + color: white; |
| 64 | + background-color: darkred; |
| 65 | + z-index: 5000; |
| 66 | +} |
| 67 | +.mw-tzpicker-marker.selected { |
| 68 | + background-color: red !important; |
| 69 | + -moz-box-shadow: 1px 1px 4px #f88; |
| 70 | + -webkit-box-shadow: 1px 1px 4px #f88; |
| 71 | + box-shadow: 1px 1px 4px #f88; |
| 72 | + z-index: 9999; |
| 73 | +} |
| 74 | +.mw-tzpicker-marker.far { |
| 75 | + background-color: #978b93; |
| 76 | + z-index: 2000; |
| 77 | +} |
Index: trunk/extensions/TimeZonePicker/resources/ext.tzpicker.js |
— | — | @@ -0,0 +1,312 @@ |
| 2 | +/** |
| 3 | + * TimeZonePicker extension |
| 4 | + * @copyright 2011 Brion Vibber <brion@pobox.com> |
| 5 | + */ |
| 6 | + |
| 7 | +(function($, mw) { |
| 8 | + |
| 9 | +// Run our setup after everything's done so we have DOM and data ready :D |
| 10 | +$(function() { |
| 11 | + |
| 12 | +var mapState = { |
| 13 | + nearest: null, |
| 14 | + selected: null, |
| 15 | + offset: 0, |
| 16 | + mapOffset: 0, |
| 17 | + mouseThreshold: 50, |
| 18 | + zoom: 1, |
| 19 | + points: [] |
| 20 | +}; |
| 21 | + |
| 22 | +var hhmmToMinutes = function(hhmm) { |
| 23 | + var matches = /^\s*([+-]?)(\d+)(?::(\d\d))?\s*$/.exec(hhmm); |
| 24 | + if (!matches) { |
| 25 | + return 0; |
| 26 | + } |
| 27 | + |
| 28 | + var plusminus = matches[1]; |
| 29 | + var hours = parseInt(matches[2], 10) || 0; |
| 30 | + var mins = parseInt(matches[3], 10) || 0; |
| 31 | + |
| 32 | + var mult = (plusminus == '-') ? -1 : 1; |
| 33 | + var minutes = mult * (hours * 60 + mins); |
| 34 | + return minutes; |
| 35 | +}; |
| 36 | + |
| 37 | +/** |
| 38 | + * Pull just the zones that match our current offset. |
| 39 | + * |
| 40 | + * @param localOffset: int |
| 41 | + * @return Array of TZ info objects |
| 42 | + */ |
| 43 | +var matchingZones = function(localOffset) { |
| 44 | + // A list of {name, offset, location} |
| 45 | + // location is {country_code, latitude, longitude, comments} |
| 46 | + // @fixme read these via AJAX so we only have to ask for matching ones? |
| 47 | + var zoneInfo = window.mw_ext_tzpicker_ZoneInfo; |
| 48 | + //return zoneInfo; // uncomment this to try showing all zones. not yet ideal |
| 49 | + |
| 50 | + var zones = []; |
| 51 | + $.each(zoneInfo, function() { |
| 52 | + if (this.offset == localOffset) { |
| 53 | + zones.push(this); |
| 54 | + } |
| 55 | + }); |
| 56 | + return zones; |
| 57 | +}; |
| 58 | + |
| 59 | +var plotLabel = function( marker ) { |
| 60 | + var labelNode = marker.data('label'); |
| 61 | + if (labelNode == null) { |
| 62 | + var zone = marker.data('zone'); |
| 63 | + var sx = marker.data('sx'); |
| 64 | + var sy = marker.data('sy'); |
| 65 | + |
| 66 | + var label = $( '<div class="mw-tzpicker-label"></div>' ); |
| 67 | + label.text( zone.name ); |
| 68 | + label.data('zone', zone); |
| 69 | + label.css('left', (sx + 16) + 'px'); |
| 70 | + label.css('top', (sy - 8) + 'px'); |
| 71 | + $('#mw-tzpicker-map').append(label); |
| 72 | + |
| 73 | + marker.data('label', label[0]); |
| 74 | + } |
| 75 | +}; |
| 76 | + |
| 77 | +/** |
| 78 | + * @param {object} zone |
| 79 | + * @return DOMElement |
| 80 | + */ |
| 81 | +var plotZone = function( zone ) { |
| 82 | + //var width = 360; |
| 83 | + //var height = 280; //280; |
| 84 | + //var centerX = 180; |
| 85 | + //var centerY = 280 / 2; |
| 86 | + var width = 720; |
| 87 | + var height = 720; |
| 88 | + var centerX = width / 2; |
| 89 | + var centerY = 558 / 2; |
| 90 | + var offset = mapState.mapOffset; |
| 91 | + |
| 92 | + var lon = zone.location.longitude; |
| 93 | + var lat = zone.location.latitude; |
| 94 | + if (lat == -90.0) { |
| 95 | + // hack for south pole |
| 96 | + lat = -78.5; // fit it in the cut-off mercator :P |
| 97 | + lon = 180; |
| 98 | + } |
| 99 | + |
| 100 | + // Normalize longitude for offset & wraparound |
| 101 | + lon = lon - offset; |
| 102 | + if (lon < -180) { |
| 103 | + lon += 360; |
| 104 | + } else if (lon > 180) { |
| 105 | + lon -= 360; |
| 106 | + } |
| 107 | + |
| 108 | + // Mercator projection per http://en.wikipedia.org/wiki/Mercator_projection#Mathematics_of_the_projection |
| 109 | + var lat_rad = lat * Math.PI / 180.0; |
| 110 | + var x = lon / 180; |
| 111 | + var y = 0.5 * Math.log( |
| 112 | + (1 + Math.sin(lat_rad)) / |
| 113 | + (1 - Math.sin(lat_rad)) |
| 114 | + ); |
| 115 | + |
| 116 | + var sx = ((x * width / 2.0) + centerX) * mapState.zoom; |
| 117 | + var sy = ((y * height / -6.0) + centerY) * mapState.zoom; |
| 118 | + if (mapState.zoom > 1) { |
| 119 | + sx -= 360; |
| 120 | + } |
| 121 | + |
| 122 | + var marker = $('<div class="mw-tzpicker-marker"></div>') |
| 123 | + .data('zone', zone) |
| 124 | + .data('sx', sx) |
| 125 | + .data('sy', sy) |
| 126 | + .css('left', (sx - 4) + 'px') |
| 127 | + .css('top', (sy - 4) + 'px'); |
| 128 | + if (parseInt(zone.offset) != parseInt(mapState.offset)) { |
| 129 | + marker.addClass('far'); |
| 130 | + } |
| 131 | + $('#mw-tzpicker-map').append(marker); |
| 132 | + |
| 133 | + // hack hack |
| 134 | + /* |
| 135 | + if (className) { |
| 136 | + plotLabel(marker); |
| 137 | + } |
| 138 | + */ |
| 139 | + mapState.points.push({ |
| 140 | + x: sx, |
| 141 | + y: sy, |
| 142 | + zone: zone, |
| 143 | + marker: marker[0] |
| 144 | + }); |
| 145 | + return marker[0]; |
| 146 | +}; |
| 147 | + |
| 148 | +var plotMatchingZones = function( localOffset, selectedName ) { |
| 149 | + var map = $('#mw-tzpicker-map'); |
| 150 | + map.empty(); |
| 151 | + mapState.offset = localOffset; |
| 152 | + mapState.points = []; |
| 153 | + |
| 154 | + var overlay = $('<div id="mw-tzpicker-overlay"></div>'); |
| 155 | + var idealDegrees = localOffset * 360 / (24 * 60); |
| 156 | + var idealPixels = idealDegrees * 2 * mapState.zoom; |
| 157 | + if (mapState.zoom > 1) { |
| 158 | + idealPixels += 360; |
| 159 | + } |
| 160 | + map.css('background-position', (-1 * idealPixels) + 'px 0'); |
| 161 | + mapState.mapOffset = idealDegrees; |
| 162 | + map.append(overlay); |
| 163 | + |
| 164 | + var zones = matchingZones(localOffset); |
| 165 | + $.each(zones, function() { |
| 166 | + var markerNode = plotZone(this); |
| 167 | + if (selectedName == this.name) { |
| 168 | + mapState.selected = markerNode; |
| 169 | + $(markerNode).addClass('selected'); |
| 170 | + plotLabel($(markerNode)); |
| 171 | + } |
| 172 | + }); |
| 173 | +}; |
| 174 | + |
| 175 | +var selectZoneByName = function(zoneName) { |
| 176 | + $('#mw-input-wptimecorrection option').each(function() { |
| 177 | + var data = $(this).val().split('|'); |
| 178 | + if( data[0] == 'ZoneInfo' ) { |
| 179 | + var offset = data[1]; |
| 180 | + var name = data[2]; |
| 181 | + if (name == zoneName) { |
| 182 | + $(this).attr('selected', true); |
| 183 | + $('#mw-input-wptimecorrection').change(); |
| 184 | + } |
| 185 | + } |
| 186 | + }); |
| 187 | +}; |
| 188 | + |
| 189 | +var plotLocalZones = function() { |
| 190 | + var localClock = new Date(); |
| 191 | + |
| 192 | + // Note that Date.getTimezoneOffset returns the inverse of what we use |
| 193 | + // elsewhere: UTC+1:00 gives -60 minutes. Convert back to match ZoneInfo... |
| 194 | + var localOffset = -1 * localClock.getTimezoneOffset(); |
| 195 | + plotMatchingZones(localOffset); |
| 196 | +}; |
| 197 | + |
| 198 | +var findNearestMarker = function(x, y) { |
| 199 | + var markers = $('#mw-tzpicker-map .mw-tzpicker-marker'); |
| 200 | + var nearest = null; |
| 201 | + var min2 = mapState.mouseThreshold * mapState.mouseThreshold; |
| 202 | + |
| 203 | + var points = mapState.points; |
| 204 | + var n = points.length; |
| 205 | + for (var i = 0; i < n; i++) { |
| 206 | + var point = points[i]; |
| 207 | + var dx = point.x - x, dy = point.y - y; |
| 208 | + var dx2 = dx * dx, dy2 = dy * dy; |
| 209 | + if (dx2 > min2 || dy2 > min2) { |
| 210 | + continue; |
| 211 | + } |
| 212 | + var dist2 = dx2 + dy2; |
| 213 | + if (dist2 < min2) { |
| 214 | + min2 = dist2; |
| 215 | + nearest = point.marker; |
| 216 | + } |
| 217 | + } |
| 218 | + return nearest; |
| 219 | +}; |
| 220 | + |
| 221 | +// Maaaagic |
| 222 | +var setupTimezones = function() { |
| 223 | + $('#mw-htmlform-timeoffset tbody') |
| 224 | + .append('<tr class="mw-tzpicker-row"><td>' + |
| 225 | + '<td class="mw-input">' + |
| 226 | + '<div id="mw-tzpicker">' + |
| 227 | + '<div id="mw-tzpicker-map"></div>' + |
| 228 | + '</div>' + |
| 229 | + '</td></tr>'); |
| 230 | + |
| 231 | + var map = $('#mw-tzpicker-map'); |
| 232 | + if (mapState.zoom > 1) { |
| 233 | + map.addClass('zoom'); |
| 234 | + } |
| 235 | + map.mousemove(function(e) { |
| 236 | + var offset = map.offset(); |
| 237 | + var x = e.pageX - offset.left; |
| 238 | + var y = e.pageY - offset.top; |
| 239 | + |
| 240 | + var nearest = findNearestMarker(x, y, 100); |
| 241 | + if (nearest) { |
| 242 | + var marker = $(nearest); |
| 243 | + plotLabel(marker); |
| 244 | + marker.addClass('selected'); |
| 245 | + } |
| 246 | + if (mapState.nearest && mapState.nearest != nearest && mapState.nearest != mapState.selected) { |
| 247 | + var oldMarker = $(mapState.nearest); |
| 248 | + oldMarker.removeClass('selected'); |
| 249 | + var oldLabel = oldMarker.data('label'); |
| 250 | + if (oldLabel) { |
| 251 | + $(oldMarker).data('label', null); |
| 252 | + $(oldLabel).remove(); |
| 253 | + } |
| 254 | + } |
| 255 | + mapState.nearest = nearest; |
| 256 | + }); |
| 257 | + map.click(function(e) { |
| 258 | + var offset = map.offset(); |
| 259 | + var x = e.pageX - offset.left; |
| 260 | + var y = e.pageY - offset.top; |
| 261 | + |
| 262 | + var nearest = findNearestMarker(x, y, 100); |
| 263 | + if (nearest && nearest != mapState.selected) { |
| 264 | + var marker = $(nearest); |
| 265 | + var zone = marker.data('zone'); |
| 266 | + selectZoneByName(zone.name); |
| 267 | + } |
| 268 | + }); |
| 269 | + map.dblclick(function(e) { |
| 270 | + if (mapState.zoom == 1) { |
| 271 | + mapState.zoom = 2; |
| 272 | + map.addClass('zoom'); |
| 273 | + } else if (mapState.zoom == 2) { |
| 274 | + mapState.zoom = 1; |
| 275 | + map.removeClass('zoom'); |
| 276 | + } |
| 277 | + if (mapState.selected) { |
| 278 | + var zone = $(mapState.selected).data('zone'); |
| 279 | + plotMatchingZones(zone.offset, zone.name); |
| 280 | + } else { |
| 281 | + plotLocalZones(); |
| 282 | + } |
| 283 | + }); |
| 284 | + |
| 285 | + var selector = $('#mw-input-wptimecorrection'); |
| 286 | + var ping = function() { |
| 287 | + var data = $(this).val().split('|'); |
| 288 | + if( data[0] == 'ZoneInfo' ) { |
| 289 | + var offset = data[1]; |
| 290 | + var name = data[2]; |
| 291 | + plotMatchingZones(offset, name); |
| 292 | + } else if (data[0] == 'guess') { |
| 293 | + plotLocalZones(); |
| 294 | + } else { |
| 295 | + var hhmm = $('#mw-input-wptimecorrection-other').val(); |
| 296 | + var minutes = hhmmToMinutes(hhmm); |
| 297 | + plotMatchingZones(minutes); |
| 298 | + } |
| 299 | + }; |
| 300 | + selector.change(function() { |
| 301 | + // horrible hack! slight delay to get the server offset |
| 302 | + window.setTimeout(function() { |
| 303 | + ping.call(selector[0]); |
| 304 | + }, 50); |
| 305 | + }); |
| 306 | + ping.call(selector[0]); |
| 307 | +}; |
| 308 | + |
| 309 | +setupTimezones(); |
| 310 | + |
| 311 | +}); |
| 312 | + |
| 313 | +})(jQuery, mediaWiki); |
Index: trunk/extensions/TimeZonePicker/resources/overlay-1440.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/TimeZonePicker/resources/overlay-1440.png |
___________________________________________________________________ |
Added: svn:mime-type |
1 | 314 | + application/octet-stream |
Index: trunk/extensions/TimeZonePicker/resources/overlay-720.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/TimeZonePicker/resources/overlay-720.png |
___________________________________________________________________ |
Added: svn:mime-type |
2 | 315 | + application/octet-stream |
Index: trunk/extensions/TimeZonePicker/README |
— | — | @@ -0,0 +1,32 @@ |
| 2 | +This extension adds an experimental time zone picker map to Special:Preference's date/time panel. |
| 3 | + |
| 4 | +PHP's date-and-time functions pass through latitude and longitude coordinates from the system |
| 5 | +timezone database, which we use here to plot on a map. |
| 6 | + |
| 7 | +To prune things down, only zone settings that match the currently selected offset are shown |
| 8 | +on the map; this lets us show what likely candidates are available that match either the |
| 9 | +configured server offset or the browser settings, then pick one from the map. |
| 10 | + |
| 11 | +Selecting a new setting (a specific zone or the server, guess, or other options) from the |
| 12 | +drop-down updates the map automatically, and centers the view on the selected offset. |
| 13 | + |
| 14 | +The map can be zoomed in 2x by double-clicking; this may require vertical scrolling in some |
| 15 | +instances. |
| 16 | + |
| 17 | +Todo: |
| 18 | +* pretty sure server default isn't being handled right if not UTC |
| 19 | +* clean up rambling code |
| 20 | +* find a cleaner way to get non-current items showing (can show them but it makes the map much busier) |
| 21 | +* redo the map refreshes so we're not removing and recreating everything when you click something already on the map! |
| 22 | +* make map zoom work on iPad (?) |
| 23 | +* show the TZ offset in HH:MM clearly at the top of the map, with selectors to jump over to other zones for browsing :D |
| 24 | +* South Pole entry is still a little off |
| 25 | +* scroll automatically in zoomed mode |
| 26 | +* clearer controls for zoom |
| 27 | +* (perhaps) use geolocation info to help pick something |
| 28 | + |
| 29 | +Note on the location information: we do *not* have boundaries available, and even if we did it wouldn't |
| 30 | +be wise to use them as borders are in dispute in a number of countries. Available locations are for the |
| 31 | +cities that are representative. |
| 32 | + |
| 33 | +The map has a shading overlay that highlights a fairly generic range that approximates the size of a time zone. |
Index: trunk/extensions/TimeZonePicker/TimeZonePicker.php |
— | — | @@ -0,0 +1,42 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Experimental next-gen timezone picker |
| 6 | + * http://www.mediawiki.org/wiki/Extension:TimeZonePicker |
| 7 | + * |
| 8 | + * @copyright 2011 Brion Vibber <brion@pobox.com> |
| 9 | + * |
| 10 | + * MediaWiki-side code is GPL v2 or later. |
| 11 | + * |
| 12 | + * World map image based on http://commons.wikimedia.org/wiki/File:Mercator-projection.jpg |
| 13 | + * Source image is from NASA's Earth Observatory "Blue Marble" series. (Public domain) |
| 14 | + */ |
| 15 | + |
| 16 | +$wgExtensionCredits['other'][] = array( |
| 17 | + 'path' => __FILE__, |
| 18 | + 'name' => 'TimeZonePicker', |
| 19 | + 'author' => array( 'Brion Vibber' ), |
| 20 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:TimeZonePicker', |
| 21 | +); |
| 22 | +$wgExtensionMessagesFiles['SVGEdit'] = dirname(__FILE__) . '/SVGEdit.i18n.php'; |
| 23 | + |
| 24 | +$wgHooks['BeforePageDisplay'][] = 'TimeZonePickerHooks::beforePageDisplay'; |
| 25 | + |
| 26 | +$wgAutoloadClasses['TimeZonePickerHooks'] = dirname( __FILE__ ) . '/TimeZonePicker.hooks.php'; |
| 27 | + |
| 28 | +$myResourceTemplate = array( |
| 29 | + 'localBasePath' => dirname( __FILE__ ) . '/resources', |
| 30 | + 'remoteExtPath' => 'TimeZonePicker/resources', |
| 31 | + 'group' => 'ext.tzpicker', |
| 32 | +); |
| 33 | +$wgResourceModules['ext.tzpicker'] = $myResourceTemplate + array( |
| 34 | + 'scripts' => array( |
| 35 | + 'ext.tzpicker.js', |
| 36 | + ), |
| 37 | + 'styles' => array( |
| 38 | + 'ext.tzpicker.css', |
| 39 | + ), |
| 40 | + 'dependencies' => array( |
| 41 | + 'mediawiki.special.preferences' |
| 42 | + ) |
| 43 | +); |
Property changes on: trunk/extensions/TimeZonePicker |
___________________________________________________________________ |
Added: svn:ignore |
1 | 44 | + .git |