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