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 canvas_style = { 184 position: 'absolute', 185 left: 0, 186 top: 0, 187 padding: 0, 188 border: 0 189 }; 190 191 var ie_hax_done = false; 192 $.fn.maphilight = function(opts) { 193 opts = $.extend({}, $.fn.maphilight.defaults, opts); 194 195 if(!has_canvas && !ie_hax_done) { 196 $(window).ready(function() { 197 document.namespaces.add("v", "urn:schemas-microsoft-com:vml"); 198 var style = document.createStyleSheet(); 199 var shapes = ['shape','rect', 'oval', 'circ', 'fill', 'stroke', 'imagedata', 'group','textbox']; 200 $.each(shapes, 201 function() { 202 style.addRule('v\\:' + this, "behavior: url(#default#VML); antialias:true"); 203 } 204 ); 205 }); 206 ie_hax_done = true; 207 } 208 209 return this.each(function() { 210 var img, wrap, options, map, canvas, canvas_always, mouseover, highlighted_shape, usemap; 211 img = $(this); 212 213 if(!is_image_loaded(this)) { 214 // If the image isn't fully loaded, this won't work right. Try again later. 215 return window.setTimeout(function() { 216 img.maphilight(opts); 217 }, 200); 218 } 219 220 options = $.extend({}, opts, $.metadata ? img.metadata() : false, img.data('maphilight')); 221 222 // jQuery bug with Opera, results in full-url#usemap being returned from jQuery's attr. 223 // So use raw getAttribute instead. 224 usemap = img.get(0).getAttribute('usemap'); 225 226 if (!usemap) { 227 return 228 } 229 230 map = $('map[name="'+usemap.substr(1)+'"]'); 231 232 if(!(img.is('img,input[type="image"]') && usemap && map.size() > 0)) { 233 return; 234 } 235 236 if(img.hasClass('maphilighted')) { 237 // We're redrawing an old map, probably to pick up changes to the options. 238 // Just clear out all the old stuff. 239 var wrapper = img.parent(); 240 img.insertBefore(wrapper); 241 wrapper.remove(); 242 $(map).unbind('.maphilight').find('area[coords]').unbind('.maphilight'); 243 } 244 245 // IE8 gets the image size wrong =/ 246 var nWidth = this.width; 247 var nHeight = this.height; 248 if (document.body.className.indexOf('IE8') != -1) { 249 nWidth = 289; 250 nHeight = 324; 251 } 252 253 wrap = $('<div></div>').css({ 254 display:'block', 255 background:'url("'+this.src+'")', 256 position:'relative', 257 padding:0, 258 width: nWidth, 259 height: nHeight 260 }); 261 if(options.wrapClass) { 262 if(options.wrapClass === true) { 263 wrap.addClass($(this).attr('class')); 264 } else { 265 wrap.addClass(options.wrapClass); 266 } 267 } 268 img.before(wrap).css('opacity', 0).css(canvas_style).remove(); 269 if(has_VML) { img.css('filter', 'Alpha(opacity=0)'); } 270 wrap.append(img); 271 272 canvas = create_canvas_for(this); 273 $(canvas).css(canvas_style); 274 canvas.height = this.height; 275 canvas.width = this.width; 276 277 mouseover = function(e) { 278 var shape, area_options; 279 area_options = options_from_area(this, options); 280 if( 281 !area_options.neverOn 282 && 283 !area_options.alwaysOn 284 ) { 285 shape = shape_from_area(this); 286 add_shape_to(canvas, shape[0], shape[1], area_options, "highlighted"); 287 if(area_options.groupBy) { 288 var areas; 289 // two ways groupBy might work; attribute and selector 290 if(/^[a-zA-Z][\-a-zA-Z]+$/.test(area_options.groupBy)) { 291 areas = map.find('area['+area_options.groupBy+'="'+$(this).attr(area_options.groupBy)+'"]'); 292 } else {
293 areas = map.find(area_options.groupBy); 294 } 295 var first = this; 296 areas.each(function() { 297 if(this != first) { 298 var subarea_options = options_from_area(this, options); 299 if(!subarea_options.neverOn && !subarea_options.alwaysOn) { 300 var shape = shape_from_area(this); 301 add_shape_to(canvas, shape[0], shape[1], subarea_options, "highlighted"); 302 } 303 } 304 }); 305 } 306 // workaround for IE7, IE8 not rendering the final rectangle in a group 307 if(!has_canvas) { 308 $(canvas).append('<v:rect></v:rect>'); 309 } 310 } 311 } 312 313 $(map).bind('alwaysOn.maphilight', function() { 314 // Check for areas with alwaysOn set. These are added to a *second* canvas, 315 // which will get around flickering during fading. 316 if(canvas_always) { 317 clear_canvas(canvas_always); 318 } 319 if(!has_canvas) { 320 $(canvas).empty(); 321 } 322 $(map).find('area[coords]').each(function() { 323 var shape, area_options; 324 area_options = options_from_area(this, options); 325 if(area_options.alwaysOn) { 326 if(!canvas_always && has_canvas) { 327 canvas_always = create_canvas_for(img[0]); 328 $(canvas_always).css(canvas_style); 329 canvas_always.width = img[0].width; 330 canvas_always.height = img[0].height; 331 img.before(canvas_always); 332 } 333 area_options.fade = area_options.alwaysOnFade; // alwaysOn shouldn't fade in initially 334 shape = shape_from_area(this); 335 if (has_canvas) { 336 add_shape_to(canvas_always, shape[0], shape[1], area_options, ""); 337 } else { 338 add_shape_to(canvas, shape[0], shape[1], area_options, ""); 339 } 340 } 341 }); 342 }); 343 344 $(map).trigger('alwaysOn.maphilight').find('area[coords]') 345 .bind('mouseover.maphilight', mouseover) 346 .bind('mouseout.maphilight', function(e) { clear_canvas(canvas); }); 347 348 img.before(canvas); // if we put this after, the mouseover events wouldn't fire. 349 350 img.addClass('maphilighted'); 351 }); 352 }; 353 $.fn.maphilight.defaults = { 354 fill: true, 355 fillColor: '000000', 356 fillOpacity: 0.2, 357 stroke: true, 358 strokeColor: 'ff0000', 359 strokeOpacity: 1, 360 strokeWidth: 1, 361 fade: true, 362 alwaysOn: false, 363 neverOn: false, 364 groupBy: false, 365 wrapClass: true, 366 // plenty of shadow: 367 shadow: false, 368 shadowX: 0, 369 shadowY: 0, 370 shadowRadius: 6, 371 shadowColor: '000000', 372 shadowOpacity: 0.8, 373 shadowPosition: 'outside', 374 shadowFrom: false 375 }; 376})(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.