source: other-projects/nz-flag-design/trunk/design-2d/Original editor.method.ac/editor/icons/jquery.svgicons.js@ 29468

Last change on this file since 29468 was 29468, checked in by sjs49, 9 years ago

Initial commit for editor.method.ac for flag design

  • Property svn:executable set to *
File size: 13.3 KB
Line 
1/*
2 * SVG Icon Loader 2.0
3 *
4 * jQuery Plugin for loading SVG icons from a single file
5 *
6 * Copyright (c) 2009 Alexis Deveria
7 * http://a.deveria.com
8 *
9 * Apache 2 License
10
11How to use:
12
131. Create the SVG master file that includes all icons:
14
15The master SVG icon-containing file is an SVG file that contains
16<g> elements. Each <g> element should contain the markup of an SVG
17icon. The <g> element has an ID that should
18correspond with the ID of the HTML element used on the page that should contain
19or optionally be replaced by the icon. Additionally, one empty element should be
20added at the end with id "svg_eof".
21
222. Optionally create fallback raster images for each SVG icon.
23
243. Include the jQuery and the SVG Icon Loader scripts on your page.
25
264. Run $.svgIcons() when the document is ready:
27
28$.svgIcons( file [string], options [object literal]);
29
30File is the location of a local SVG or SVGz file.
31
32All options are optional and can include:
33
34- 'w (number)': The icon widths
35
36- 'h (number)': The icon heights
37
38- 'fallback (object literal)': List of raster images with each
39 key being the SVG icon ID to replace, and the value the image file name.
40
41- 'fallback_path (string)': The path to use for all images
42 listed under "fallback"
43
44- 'replace (boolean)': If set to true, HTML elements will be replaced by,
45 rather than include the SVG icon.
46
47- 'placement (object literal)': List with selectors for keys and SVG icon ids
48 as values. This provides a custom method of adding icons.
49
50- 'resize (object literal)': List with selectors for keys and numbers
51 as values. This allows an easy way to resize specific icons.
52
53- 'callback (function)': A function to call when all icons have been loaded.
54 Includes an object literal as its argument with as keys all icon IDs and the
55 icon as a jQuery object as its value.
56
57- 'id_match (boolean)': Automatically attempt to match SVG icon ids with
58 corresponding HTML id (default: true)
59
60- 'no_img (boolean)': Prevent attempting to convert the icon into an <img>
61 element (may be faster, help for browser consistency)
62
63- 'svgz (boolean)': Indicate that the file is an SVGZ file, and thus not to
64 parse as XML. SVGZ files add compression benefits, but getting data from
65 them fails in Firefox 2 and older.
66
675. To access an icon at a later point without using the callback, use this:
68 $.getSvgIcon(id (string));
69
70This will return the icon (as jQuery object) with a given ID.
71
726. To resize icons at a later point without using the callback, use this:
73 $.resizeSvgIcons(resizeOptions) (use the same way as the "resize" parameter)
74
75
76Example usage #1:
77
78$(function() {
79 $.svgIcons('my_icon_set.svg'); // The SVG file that contains all icons
80 // No options have been set, so all icons will automatically be inserted
81 // into HTML elements that match the same IDs.
82});
83
84Example usage #2:
85
86$(function() {
87 $.svgIcons('my_icon_set.svg', { // The SVG file that contains all icons
88 callback: function(icons) { // Custom callback function that sets click
89 // events for each icon
90 $.each(icons, function(id, icon) {
91 icon.click(function() {
92 alert('You clicked on the icon with id ' + id);
93 });
94 });
95 }
96 }); //The SVG file that contains all icons
97});
98
99Example usage #3:
100
101$(function() {
102 $.svgIcons('my_icon_set.svgz', { // The SVGZ file that contains all icons
103 w: 32, // All icons will be 32px wide
104 h: 32, // All icons will be 32px high
105 fallback_path: 'icons/', // All fallback files can be found here
106 fallback: {
107 '#open_icon': 'open.png', // The "open.png" will be appended to the
108 // HTML element with ID "open_icon"
109 '#close_icon': 'close.png',
110 '#save_icon': 'save.png'
111 },
112 placement: {'.open_icon','open'}, // The "open" icon will be added
113 // to all elements with class "open_icon"
114 resize: function() {
115 '#save_icon .svg_icon': 64 // The "save" icon will be resized to 64 x 64px
116 },
117
118 callback: function(icons) { // Sets background color for "close" icon
119 icons['close'].css('background','red');
120 },
121
122 svgz: true // Indicates that an SVGZ file is being used
123
124 })
125});
126
127*/
128
129
130(function($) {
131 var svg_icons = {}, fixIDs;
132
133 $.svgIcons = function(file, opts) {
134 var svgns = "http://www.w3.org/2000/svg",
135 xlinkns = "http://www.w3.org/1999/xlink",
136 icon_w = opts.w?opts.w : 24,
137 icon_h = opts.h?opts.h : 24,
138 elems, svgdoc, testImg,
139 icons_made = false, data_loaded = false, load_attempts = 0,
140 ua = navigator.userAgent, isOpera = !!window.opera, isSafari = (ua.indexOf('Safari/') > -1 && ua.indexOf('Chrome/')==-1),
141 data_pre = 'data:image/svg+xml;charset=utf-8;base64,';
142
143 if(opts.svgz) {
144 var data_el = $('<object data="' + file + '" type=image/svg+xml>').appendTo('body').hide();
145 try {
146 svgdoc = data_el[0].contentDocument;
147 data_el.load(getIcons);
148 getIcons(0, true); // Opera will not run "load" event if file is already cached
149 } catch(err1) {
150 useFallback();
151 }
152 } else {
153 var parser = new DOMParser();
154 $.ajax({
155 url: file,
156 dataType: 'string',
157 success: function(data) {
158 if(!data) {
159 $(useFallback);
160 return;
161 }
162 svgdoc = parser.parseFromString(data, "text/xml");
163 $(function() {
164 getIcons('ajax');
165 });
166 },
167 error: function(err) {
168 // TODO: Fix Opera widget icon bug
169 if(window.opera) {
170 $(function() {
171 useFallback();
172 });
173 } else {
174 if(err.responseText) {
175 svgdoc = parser.parseFromString(err.responseText, "text/xml");
176 if(!svgdoc.childNodes.length) {
177 $(useFallback);
178 }
179 $(function() {
180 getIcons('ajax');
181 });
182 } else {
183 $(useFallback);
184 }
185 }
186 }
187 });
188 }
189
190 function getIcons(evt, no_wait) {
191 if(evt !== 'ajax') {
192 if(data_loaded) return;
193 // Webkit sometimes says svgdoc is undefined, other times
194 // it fails to load all nodes. Thus we must make sure the "eof"
195 // element is loaded.
196 svgdoc = data_el[0].contentDocument; // Needed again for Webkit
197 var isReady = (svgdoc && svgdoc.getElementById('svg_eof'));
198 if(!isReady && !(no_wait && isReady)) {
199 load_attempts++;
200 if(load_attempts < 50) {
201 setTimeout(getIcons, 20);
202 } else {
203 useFallback();
204 data_loaded = true;
205 }
206 return;
207 }
208 data_loaded = true;
209 }
210
211 elems = $(svgdoc.firstChild).children(); //.getElementsByTagName('foreignContent');
212
213 if(!opts.no_img) {
214 var testSrc = data_pre + 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNzUiIGhlaWdodD0iMjc1Ij48L3N2Zz4%3D';
215
216 testImg = $(new Image()).attr({
217 src: testSrc,
218 width: 0,
219 height: 0
220 }).appendTo('body')
221 .load(function () {
222 // Safari 4 crashes, Opera and Chrome don't
223 makeIcons(true);
224 }).error(function () {
225 makeIcons();
226 });
227 } else {
228 setTimeout(function() {
229 if(!icons_made) makeIcons();
230 },500);
231 }
232 }
233
234 var setIcon = function(target, icon, id, setID) {
235 if(isOpera) icon.css('visibility','hidden');
236 if(opts.replace) {
237 if(setID) icon.attr('id',id);
238 var cl = target.attr('class');
239 if(cl) icon.attr('class','svg_icon '+cl);
240 target.replaceWith(icon);
241 } else {
242
243 target.append(icon);
244 }
245 if(isOpera) {
246 setTimeout(function() {
247 icon.removeAttr('style');
248 },1);
249 }
250 }
251
252 var addIcon = function(icon, id) {
253 if(opts.id_match === undefined || opts.id_match !== false) {
254 setIcon(holder, icon, id, true);
255 }
256 svg_icons[id] = icon;
257 }
258
259 function makeIcons(toImage, fallback) {
260 if(icons_made) return;
261 if(opts.no_img) toImage = false;
262 var holder;
263
264 if(toImage) {
265 var temp_holder = $(document.createElement('div'));
266 temp_holder.hide().appendTo('body');
267 }
268 if(fallback) {
269 var path = opts.fallback_path?opts.fallback_path:'';
270 $.each(fallback, function(id, imgsrc) {
271 holder = $('#' + id);
272 var icon = $(new Image())
273 .attr({
274 'class':'svg_icon',
275 src: path + imgsrc,
276 'width': icon_w,
277 'height': icon_h,
278 'alt': 'icon'
279 });
280
281 addIcon(icon, id);
282 });
283 } else {
284 var len = elems.length;
285 for(var i = 0; i < len; i++) {
286 var elem = elems[i];
287 var id = elem.id;
288 if(id === 'svg_eof') break;
289 holder = $('#' + id);
290 var svg = elem.getElementsByTagNameNS(svgns, 'svg')[0];
291 var svgroot = document.createElementNS(svgns, "svg");
292 svgroot.setAttributeNS(svgns, 'viewBox', [0,0,icon_w,icon_h].join(' '));
293 // Make flexible by converting width/height to viewBox
294 var w = svg.getAttribute('width');
295 var h = svg.getAttribute('height');
296 svg.removeAttribute('width');
297 svg.removeAttribute('height');
298
299 var vb = svg.getAttribute('viewBox');
300 if(!vb) {
301 svg.setAttribute('viewBox', [0,0,w,h].join(' '));
302 }
303
304 // Not using jQuery to be a bit faster
305 svgroot.setAttribute('xmlns', svgns);
306 svgroot.setAttribute('width', icon_w);
307 svgroot.setAttribute('height', icon_h);
308 svgroot.setAttribute("xmlns:xlink", xlinkns);
309 svgroot.setAttribute("class", 'svg_icon');
310
311 // Without cloning, Firefox will make another GET request.
312 // With cloning, causes issue in Opera/Win/Non-EN
313 if(!isOpera) svg = svg.cloneNode(true);
314
315 svgroot.appendChild(svg);
316
317 if(toImage) {
318 // Without cloning, Safari will crash
319 // With cloning, causes issue in Opera/Win/Non-EN
320 var svgcontent = isOpera?svgroot:svgroot.cloneNode(true);
321 temp_holder.empty().append(svgroot);
322 var str = data_pre + encode64(temp_holder.html());
323 var icon = $(new Image())
324 .attr({'class':'svg_icon', src:str});
325 } else {
326 var icon = fixIDs($(svgroot), i);
327 }
328 addIcon(icon, id);
329 }
330
331 }
332
333 if(opts.placement) {
334 $.each(opts.placement, function(sel, id) {
335 if(!svg_icons[id]) return;
336 $(sel).each(function(i) {
337 var copy = svg_icons[id].clone();
338 if(i > 0 && !toImage) copy = fixIDs(copy, i, true);
339 setIcon($(this), copy, id);
340 })
341 });
342 }
343 if(!fallback) {
344 if(toImage) temp_holder.remove();
345 if(data_el) data_el.remove();
346 if(testImg) testImg.remove();
347 }
348 if(opts.resize) $.resizeSvgIcons(opts.resize);
349 icons_made = true;
350
351 if(opts.callback) opts.callback(svg_icons);
352 }
353
354 fixIDs = function(svg_el, svg_num, force) {
355 var defs = svg_el.find('defs');
356 if(!defs.length) return svg_el;
357
358 if(isOpera) {
359 var id_elems = defs.find('*').filter(function() {
360 return !!this.id;
361 });
362 } else {
363 var id_elems = defs.find('[id]');
364 }
365
366 var all_elems = svg_el[0].getElementsByTagName('*'), len = all_elems.length;
367
368 id_elems.each(function(i) {
369 var id = this.id;
370 var no_dupes = ($(svgdoc).find('#' + id).length <= 1);
371 if(isOpera) no_dupes = false; // Opera didn't clone svg_el, so not reliable
372 // if(!force && no_dupes) return;
373 var new_id = 'x' + id + svg_num + i;
374 this.id = new_id;
375
376 var old_val = 'url(#' + id + ')';
377 var new_val = 'url(#' + new_id + ')';
378
379 for(var i = 0; i < len; i++) {
380 var elem = all_elems[i];
381 if(elem.getAttribute('fill') === old_val) {
382 elem.setAttribute('fill', new_val);
383 }
384 if(elem.getAttribute('stroke') === old_val) {
385 elem.setAttribute('stroke', new_val);
386 }
387 if(elem.getAttribute('filter') === old_val) {
388 elem.setAttribute('filter', new_val);
389 }
390 }
391 });
392 return svg_el;
393 }
394
395 function useFallback() {
396 if(file.indexOf('.svgz') != -1) {
397 var reg_file = file.replace('.svgz','.svg');
398 if(window.console) {
399 console.log('.svgz failed, trying with .svg');
400 }
401 $.svgIcons(reg_file, opts);
402 } else if(opts.fallback) {
403 makeIcons(false, opts.fallback);
404 }
405 }
406
407 function encode64(input) {
408 // base64 strings are 4/3 larger than the original string
409 if(window.btoa) return window.btoa(input);
410 var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
411 var output = new Array( Math.floor( (input.length + 2) / 3 ) * 4 );
412 var chr1, chr2, chr3;
413 var enc1, enc2, enc3, enc4;
414 var i = 0, p = 0;
415
416 do {
417 chr1 = input.charCodeAt(i++);
418 chr2 = input.charCodeAt(i++);
419 chr3 = input.charCodeAt(i++);
420
421 enc1 = chr1 >> 2;
422 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
423 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
424 enc4 = chr3 & 63;
425
426 if (isNaN(chr2)) {
427 enc3 = enc4 = 64;
428 } else if (isNaN(chr3)) {
429 enc4 = 64;
430 }
431
432 output[p++] = _keyStr.charAt(enc1);
433 output[p++] = _keyStr.charAt(enc2);
434 output[p++] = _keyStr.charAt(enc3);
435 output[p++] = _keyStr.charAt(enc4);
436 } while (i < input.length);
437
438 return output.join('');
439 }
440 }
441
442 $.getSvgIcon = function(id, uniqueClone) {
443 var icon = svg_icons[id];
444 if(uniqueClone && icon) {
445 icon = fixIDs(icon, 0, true).clone(true);
446 }
447 return icon;
448 }
449
450 $.resizeSvgIcons = function(obj) {
451 // FF2 and older don't detect .svg_icon, so we change it detect svg elems instead
452 var change_sel = !$('.svg_icon:first').length;
453 $.each(obj, function(sel, size) {
454 var arr = $.isArray(size);
455 var w = arr?size[0]:size,
456 h = arr?size[1]:size;
457 if(change_sel) {
458 sel = sel.replace(/\.svg_icon/g,'svg');
459 }
460 $(sel).each(function() {
461 this.setAttribute('width', w);
462 this.setAttribute('height', h);
463 if(window.opera && window.widget) {
464 this.parentNode.style.width = w + 'px';
465 this.parentNode.style.height = h + 'px';
466 }
467 });
468 });
469 }
470
471})(jQuery);
Note: See TracBrowser for help on using the repository browser.