1(function($) { 2 var has_VML, has_canvas, create_canvas_for, add_shape_to, clear_canvas, shape_from_area, 3 canvas_style, hex_to_decimal, css3color, is_image_loaded, options_from_area; 4 5 has_VML = document.namespaces; 6 has_canvas = !!document.createElement('canvas').getContext; 7 8 if(!(has_canvas || has_VML)) { 9 $.fn.maphilight = function() { return this; }; 10 return; 11 } 12 13 if(has_canvas) { 14 hex_to_decimal = function(hex) { 15 return Math.max(0, Math.min(parseInt(hex, 16), 255)); 16 }; 17 css3color = function(color, opacity) { 18 return 'rgba('+hex_to_decimal(color.substr(0,2))+','+hex_to_decimal(color.substr(2,2))+','+hex_to_decimal(color.substr(4,2))+','+opacity+')'; 19 }; 20 create_canvas_for = function(img) { 21 $('img[usemap]').rwdImageMaps(); 22 var c = $('<canvas></canvas>').get(0); 23 c.getContext("2d").clearRect(0, 0, c.width, c.height); 24 return c; 25 }; 26 var draw_shape = function(context, shape, coords, x_shift, y_shift) { 27 x_shift = x_shift || 0; 28 y_shift = y_shift || 0; 29 30 context.beginPath(); 31 if(shape == 'rect') { 32 // x, y, width, height 33 context.rect(coords[0] + x_shift, coords[1] + y_shift, coords[2] - coords[0], coords[3] - coords[1]); 34 } else if(shape == 'poly') { 35 context.moveTo(coords[0] + x_shift, coords[1] + y_shift); 36 for(i=2; i < coords.length; i+=2) { 37 context.lineTo(coords[i] + x_shift, coords[i+1] + y_shift); 38 } 39 } else if(shape == 'circ') { 40 // x, y, radius, startAngle, endAngle, anticlockwise 41 context.arc(coords[0] + x_shift, coords[1] + y_shift, coords[2], 0, Math.PI * 2, false); 42 } 43 context.closePath(); 44 } 45 add_shape_to = function(canvas, shape, coords, options, name) { 46 var i, context = canvas.getContext('2d'); 47 48 // Because I don't want to worry about setting things back to a base state 49 50 // Shadow has to happen first, since it's on the bottom, and it does some clip / 51 // fill operations which would interfere with what comes next. 52 if(options.shadow) { 53 context.save(); 54 if(options.shadowPosition == "inside") { 55 // Cause the following stroke to only apply to the inside of the path 56 draw_shape(context, shape, coords); 57 context.clip(); 58 } 59 60 // Redraw the shape shifted off the canvas massively so we can cast a shadow 61 // onto the canvas without having to worry about the stroke or fill (which 62 // cannot have 0 opacity or width, since they're what cast the shadow). 63 var x_shift = canvas.width * 100; 64 var y_shift = canvas.height * 100; 65 draw_shape(context, shape, coords, x_shift, y_shift); 66 67 context.shadowOffsetX = options.shadowX - x_shift; 68 context.shadowOffsetY = options.shadowY - y_shift; 69 context.shadowBlur = options.shadowRadius; 70 context.shadowColor = css3color(options.shadowColor, options.shadowOpacity); 71 72 // Now, work out where to cast the shadow from! It looks better if it's cast 73 // from a fill when it's an outside shadow or a stroke when it's an interior 74 // shadow. Allow the user to override this if they need to. 75 var shadowFrom = options.shadowFrom; 76 if (!shadowFrom) { 77 if (options.shadowPosition == 'outside') { 78 shadowFrom = 'fill'; 79 } else { 80 shadowFrom = 'stroke'; 81 } 82 } 83 if (shadowFrom == 'stroke') { 84 context.strokeStyle = "rgba(0,0,0,1)"; 85 context.stroke(); 86 } else if (shadowFrom == 'fill') { 87 context.fillStyle = "rgba(0,0,0,1)"; 88 context.fill(); 89 } 90 context.restore(); 91 92 // and now we clean up 93 if(options.shadowPosition == "outside") { 94 context.save(); 95 // Clear out the center 96 draw_shape(context, shape, coords); 97 context.globalCompositeOperation = "destination-out"; 98 context.fillStyle = "rgba(0,0,0,1);"; 99 context.fill(); 100 context.restore(); 101 } 102 } 103 104 context.save(); 105 106 draw_shape(context, shape, coords); 107 108 // fill has to come after shadow, otherwise the shadow will be drawn over the fill, 109 // which mostly looks weird when the shadow has a high opacity 110 if(options.fill) { 111 context.fillStyle = css3color(options.fillColor, options.fillOpacity); 112 context.fill(); 113 } 114 // Likewise, stroke has to come at the very end, or it'll wind up under bits of the 115 // shadow or the shadow-background if it's present. 116 if(options.stroke) { 117 context.strokeStyle = css3color(options.strokeColor, options.strokeOpacity); 118 context.lineWidth = options.strokeWidth; 119 context.stroke(); 120 } 121
122 context.restore(); 123 124 if(options.fade) { 125 $(canvas).css('opacity', 0).animate({opacity: 1}, 100); 126 } 127 }; 128 clear_canvas = function(canvas) { 129 canvas.getContext('2d').clearRect(0, 0, canvas.width,canvas.height); 130 }; 131 } else { // ie executes this code 132 create_canvas_for = function(img) { 133 return $('<var style="zoom:1;overflow:hidden;display:block;width:'+img.width+'px;height:'+img.height+'px;"></var>').get(0); 134 }; 135 add_shape_to = function(canvas, shape, coords, options, name) { 136 var fill, stroke, opacity, e; 137 fill = '<v:fill color="#'+options.fillColor+'" opacity="'+(options.fill ? options.fillOpacity : 0)+'" />'; 138 stroke = (options.stroke ? 'strokeweight="'+options.strokeWidth+'" stroked="t" strokecolor="#'+options.strokeColor+'"' : 'stroked="f"'); 139 opacity = '<v:stroke opacity="'+options.strokeOpacity+'"/>'; 140 if(shape == 'rect') { 141 e = $('<v:rect name="'+name+'" filled="t" '+stroke+' style="zoom:1;margin:0;padding:0;display:block;position:absolute;left:'+coords[0]+'px;top:'+coords[1]+'px;width:'+(coords[2] - coords[0])+'px;height:'+(coords[3] - coords[1])+'px;"></v:rect>'); 142 } else if(shape == 'poly') { 143 e = $('<v:shape name="'+name+'" filled="t" '+stroke+' coordorigin="0,0" coordsize="'+canvas.width+','+canvas.height+'" path="m '+coords[0]+','+coords[1]+' l '+coords.join(',')+' x e" style="zoom:1;margin:0;padding:0;display:block;position:absolute;top:0px;left:0px;width:'+canvas.width+'px;height:'+canvas.height+'px;"></v:shape>'); 144 } else if(shape == 'circ') { 145 e = $('<v:oval name="'+name+'" filled="t" '+stroke+' style="zoom:1;margin:0;padding:0;display:block;position:absolute;left:'+(coords[0] - coords[2])+'px;top:'+(coords[1] - coords[2])+'px;width:'+(coords[2]*2)+'px;height:'+(coords[2]*2)+'px;"></v:oval>'); 146 } 147 e.get(0).innerHTML = fill+opacity; 148 $(canvas).append(e); 149 }; 150 clear_canvas = function(canvas) { 151 $(canvas).find('[name=highlighted]').remove(); 152 }; 153 } 154 155 shape_from_area = function(area) { 156 var i, coords = area.getAttribute('coords').split(','); 157 for (i=0; i < coords.length; i++) { coords[i] = parseFloat(coords[i]); } 158 return [area.getAttribute('shape').toLowerCase().substr(0,4), coords]; 159 }; 160 161 options_from_area = function(area, options) { 162 var $area = $(area); 163 return $.extend({}, options, $.metadata ? $area.metadata() : false, $area.data('maphilight')); 164 }; 165 166 is_image_loaded = function(img) { 167 if(!img.complete) { return false; } // IE 168 if(typeof img.naturalWidth != "undefined" && img.naturalWidth == 0) { return false; } // Others 169 return true; 170 }; 171 172 canvas_style = { 173 position: 'absolute', 174 left: 0, 175 top: 0, 176 padding: 0, 177 border: 0 178 }; 179 180 var ie_hax_done = false; 181 $.fn.maphilight = function(opts) { 182 opts = $.extend({}, $.fn.maphilight.defaults, opts); 183 184 if(!has_canvas && $.browser.msie && !ie_hax_done) { 185 document.namespaces.add("v", "urn:schemas-microsoft-com:vml"); 186 var style = document.createStyleSheet(); 187 var shapes = ['shape','rect', 'oval', 'circ', 'fill', 'stroke', 'imagedata', 'group','textbox']; 188 $.each(shapes, 189 function() { 190 style.addRule('v\\:' + this, "behavior: url(#default#VML); antialias:true"); 191 } 192 ); 193 ie_hax_done = true; 194 } 195 196 return this.each(function() { 197 var img, wrap, options, map, canvas, canvas_always, mouseover, highlighted_shape, usemap; 198 img = $(this); 199 200 if(!is_image_loaded(this)) { 201 // If the image isn't fully loaded, this won't work right. Try again later. 202 return window.setTimeout(function() { 203 img.maphilight(opts); 204 }, 200); 205 } 206 var imgW = img.width(); 207 var imgH = img.height(); 208 options = $.extend({}, opts, $.metadata ? img.metadata() : false, img.data('maphilight')); 209 210 // jQuery bug with Opera, results in full-url#usemap being returned from jQuery's attr. 211 // So use raw getAttribute instead. 212 usemap = img.get(0).getAttribute('usemap'); 213 214 map = $('map[name="'+usemap.substr(1)+'"]'); 215 216 if(!(img.is('img') && usemap && map.size() > 0)) { 217 return; 218 } 219 220 if(img.hasClass('maphilighted')) { 221 // We're redrawing an old map, probably to pick up changes to the options. 222 // Just clear out all the old stuff. 223 var wrapper = img.parent(); 224 img.insertBefore(wrapper); 225 wrapper.remove(); 226 $(map).unbind('.maphilight').find('area[coords]').unbind('.maphilight'); 227 } 228 229 wrap = $('<div class="top-center-contain"></div>').css({ 230 display: 'block', 231 background: 'url("' + this.src + '")', 232 'background-size': 'cover', 233 position: 'relative', 234 padding: 0, 235 width: imgW, 236 height: imgH 237 }); 238 if(options.wrapClass) { 239 if(options.wrapClass === true) { 240 wrap.addClass($(this).attr('class')); 241 } else { 242 wrap.addClass(options.wrapClass); 243 } 244 } 245 img.before(wrap).css('opacity', 0).css(canvas_style).remove(); 246 //if($.browser.msie) { img.css('filter', 'Alpha(opacity=0)'); } 247 wrap.append(img); 248 249 canvas = create_canvas_for(this); 250 $(canvas).css(canvas_style); 251 canvas.height = imgH; 252 canvas.width = imgW;
253 console.log(this.width); 254 mouseover = function(e) { 255 var shape, area_options; 256 area_options = options_from_area(this, options); 257 if( 258 !area_options.neverOn 259 && 260 !area_options.alwaysOn 261 ) { 262 shape = shape_from_area(this); 263 add_shape_to(canvas, shape[0], shape[1], area_options, "highlighted"); 264 if(area_options.groupBy) { 265 var areas; 266 // two ways groupBy might work; attribute and selector 267 if(/^[a-zA-Z][-a-zA-Z]+$/.test(area_options.groupBy)) { 268 areas = map.find('area['+area_options.groupBy+'="'+$(this).attr(area_options.groupBy)+'"]') 269 } else { 270 areas = map.find(area_options.groupBy); 271 } 272 var first = this; 273 areas.each(function() { 274 if(this != first) { 275 var subarea_options = options_from_area(this, options); 276 if(!subarea_options.neverOn && !subarea_options.alwaysOn) { 277 var shape = shape_from_area(this); 278 add_shape_to(canvas, shape[0], shape[1], subarea_options, "highlighted"); 279 } 280 } 281 }); 282 } 283 // workaround for IE7, IE8 not rendering the final rectangle in a group 284 if(!has_canvas) { 285 $(canvas).append('<v:rect></v:rect>'); 286 } 287 } 288 } 289 290 $(map).bind('alwaysOn.maphilight', function() {
291 // Check for areas with alwaysOn set. These are added to a *second* canvas, 292 // which will get around flickering during fading. 293 if(canvas_always) { 294 clear_canvas(canvas_always) 295 } 296 if(!has_canvas) { 297 $(canvas).empty(); 298 } 299 $(map).find('area[coords]').each(function() { 300 var shape, area_options; 301 area_options = options_from_area(this, options); 302 if(area_options.alwaysOn) { 303 if(!canvas_always && has_canvas) { 304 canvas_always = create_canvas_for(img.get()); 305 $(canvas_always).css(canvas_style); 306 canvas_always.width = img.width(); 307 canvas_always.height = img.height(); 308 img.before(canvas_always); 309 } 310 area_options.fade = area_options.alwaysOnFade; // alwaysOn shouldn't fade in initially 311 shape = shape_from_area(this); 312 if (has_canvas) { 313 add_shape_to(canvas_always, shape[0], shape[1], area_options, ""); 314 } else { 315 add_shape_to(canvas, shape[0], shape[1], area_options, ""); 316 } 317 } 318 }); 319 }); 320 321 $(map).trigger('alwaysOn.maphilight').find('area[coords]') 322 .bind('mouseover.maphilight', mouseover) 323 .bind('mouseout.maphilight', function(e) { clear_canvas(canvas); });; 324 325 img.before(canvas); // if we put this after, the mouseover events wouldn't fire. 326 327 img.addClass('maphilighted'); 328 }); 329 }; 330 $.fn.maphilight.defaults = { 331 fill: true, 332 fillColor: '00000', 333 fillOpacity: 0.2, 334 stroke: true, 335 strokeColor: 'E86132', 336 strokeOpacity: 1, 337 strokeWidth: 1, 338 fade: true, 339 alwaysOn: false, 340 neverOn: false, 341 groupBy: false, 342 wrapClass: true, 343 // plenty of shadow: 344 shadow: false, 345 shadowX: 0, 346 shadowY: 0, 347 shadowRadius: 6, 348 shadowColor: '00000', 349 shadowOpacity: 0.8, 350 shadowPosition: 'outside', 351 shadowFrom: false 352 }; 353})(jQuery);
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.